From a1339df793ee293078f98915bd3335544f883fe0 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Sun, 2 May 2010 23:47:53 -0500 Subject: [PATCH 01/14] set up framework --- require.js | 149 +++++++++++++++-------------------------------------- 1 file changed, 42 insertions(+), 107 deletions(-) diff --git a/require.js b/require.js index c9df5b9..1006705 100644 --- a/require.js +++ b/require.js @@ -1,126 +1,61 @@ -/** - * @fileOverview An implentation of JavaScript modules for use in an ASP or - * WScript environment. This implementation is based on the one used in - * Narhwal - * - * @see ServerJS/Modules/SecurableModules - * @see Narwhal - * @author Nathan L Smith - * @date February 21, 2009 - */ - -/*global ActiveXObject, Response, Server, WScript, exports, require */ +// commonjscript require + +/*global ActiveXObject, Response, Server, WScript */ /*jslint evil:true */ -(function () { +(function (global) { + +var platform, fs = {}, paths = []; + +// Base setup +// ============================================================================= // Don't do anything if require is already there -if (typeof require === "function") { return; } +if (typeof global.require === "function") { return; } -/** Global exports object */ -if (typeof exports === "undefined") { exports = {}; } +// Global exports object +if (typeof global.exports === "undefined") { global.exports = {}; } -/** A print function for use in logging */ +// WScript or ASP? +if (typeof Response === "object" && typeof Response.write !== "undefined") { + platform = "asp"; +} else if (typeof WScript === "object") { platform = "wscript"; +} else { platform = "unknown"; } + +// alias for Server.mapPath on ASP +function m(path) { return platform === "asp" ? Server.mapPath(path) : path; } + +// print function function print() { var out = Array.prototype.slice.call(arguments).join(" "); - if (typeof WScript === "object") { - WScript.echo(out); - } else if (Response && typeof Response.write !== "undefined") { - Response.write(out + "
"); - } + if (platform === "wscript") { WScript.echo(out); } + else if (platform === "asp") { Response.write(out + "
"); } } -//////////////////////////////////////////////////////////////////////////////// -// Internal functions for file manipulation -//////////////////////////////////////////////////////////////////////////////// - -/** - * An implentation of readFile, which opens a file from the filesystem and - * returns its contents as a string - * @private - */ -function readFile(fileName) { - var contents, - fileSystem = new ActiveXObject("Scripting.FileSystemObject"), - mapPath = function mapPath(path) { - var isASP = typeof Server === "object" && - typeof Server.mapPath !== "undefined"; - return isASP ? Server.mapPath(path) : path; - }; - fileName = mapPath(fileName); - - if (fileSystem.fileExists(fileName)) { - try { // JScript will throw an error if the file is empty - contents = fileSystem.openTextFile(fileName).readAll(); - } catch (e) { contents = ""; } - } else { throw new Error("File " + fileName + " does not exist"); } - return contents; -} +// Filesystem +// ============================================================================= -function dirname(path) { - var raw = String(path), - match = raw.match(/^(.*)\/[^\/]+\/?$/); - if (match && match[1]) { return match[1]; } - else if (raw.charAt(0) == "/") { return "/"; } - else { return "."; } -} +fs = { + // Filesystem object + o: new ActiveXObject("Scripting.FileSystemObject") -function canonicalize(path) { - return path.replace(/[^\/]+\/\.\.\//g, "").replace(/([^\.])\.\//g, "$1"). - replace(/^\.\//g, "").replace(/\/\/+/g, "/"); -} +}; -//////////////////////////////////////////////////////////////////////////////// - -function _require(name, parentPath, loadOnce) { - var result, pwd, extensions, paths, path, searchDirectory, ext; - if (name.charAt(0) === "/") { - result = _attemptLoad(name, name, loadOnce); - if (result) { return result; } - } else { - pwd = dirname(parentPath); - extensions = (/\.\w+$/).test(name) ? [""] : require.extensions; - paths = ["."].concat(require.paths); - for (var j = 0; j < extensions.length; j++) { - ext = extensions[j]; - for (var i = 0; i < paths.length; i++) { - searchDirectory = (paths[i] === ".") ? pwd : paths[i]; - path = searchDirectory + "/" + name + ext; - result = _attemptLoad(name, path, loadOnce); - if (result) { return result; } - } - } - } - throw new Error("couldn't find " + name); -} +// Loader +// ============================================================================= + +function Loader() { -function _requireFactory(path, loadOnce) { - return function(name) { - return _require(name, path, loadOnce || false); - }; } -function _attemptLoad(name, path, loadOnce) { - path = canonicalize(path); - var module, moduleCode; - - // see if the module is already loaded - if (require.loaded[path] && loadOnce) { return require.loaded[path]; } - try { moduleCode = readFile(path); } catch (e) {} - if (typeof moduleCode !== "undefined") { - require.loaded[path] = {}; - module = new Function("require", "exports", moduleCode); - module(_requireFactory(path, true), require.loaded[path]); - return require.loaded[path]; - } - return false; +// Sandbox +// ============================================================================= + +function Sandbox() { + return function () {}; } -/** The global require function */ -require = function (name) { return _require(name, ".", true); }; +// ============================================================================ +global.require = Sandbox({ loader: Loader({ paths: paths }) }); -require.paths = ["lib"]; -require.loaded = {}; -require.extensions = [".js"]; - -})(); +})(this); From f1e62343b84de0545b13a606b1f070c7692decf4 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Tue, 4 May 2010 16:24:55 -0500 Subject: [PATCH 02/14] Started rewrite --- lib/browser/xhr.js | 6 ------ lib/system.js | 26 ----------------------- package.json | 2 +- require.js | 51 +++++++++++++++++++++++++++++----------------- test/test.js | 24 +++++++++++++--------- 5 files changed, 47 insertions(+), 62 deletions(-) delete mode 100644 lib/browser/xhr.js delete mode 100644 lib/system.js diff --git a/lib/browser/xhr.js b/lib/browser/xhr.js deleted file mode 100644 index 9574c8e..0000000 --- a/lib/browser/xhr.js +++ /dev/null @@ -1,6 +0,0 @@ -// commonjscript XMLHttpRequest client -// @see http://wiki.commonjs.org/wiki/HTTP_Client/B - -exports.XMLHttpRequest = function () { - return new ActiveXObject("MSXML2.ServerXMLHTTP"); -}; diff --git a/lib/system.js b/lib/system.js deleted file mode 100644 index e461a91..0000000 --- a/lib/system.js +++ /dev/null @@ -1,26 +0,0 @@ -// commonjscript system module - -/*global Response, WScript */ - -var platform; - -exports.engine = "jscript"; - -// The platform is either wscript (cli) or asp -if (typeof WScript === "object") { - platform = "wscript"; -} else if (typeof Response === "object" && - typeof Response.write !== "undefined") { - platform = "asp"; -} else { - platform = "unknown"; -} - -// print function -exports.print = function () { - var out = Array.prototype.slice.call(arguments).join(" "); - if (platform === "wscript") { WScript.echo(out); } - else if (platform === "asp") { Response.write(out + "
"); } -}; - -exports.stdio = { print: exports.print }; diff --git a/package.json b/package.json index c39920b..56be46b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "commonjscript", "description": "An implementation of the CommonJS API for Classic ASP and Windows Script Host", - "version": "1.0.0", + "version": "0.0.0", "maintainers": [{ "name": "Nathan L Smith", "email": "nlloyds@gmail.com", diff --git a/require.js b/require.js index 1006705..b4a3712 100644 --- a/require.js +++ b/require.js @@ -1,45 +1,58 @@ // commonjscript require -/*global ActiveXObject, Response, Server, WScript */ +/*global require, exports, ActiveXObject, Response, Server, WScript */ /*jslint evil:true */ -(function (global) { +// Globals -var platform, fs = {}, paths = []; +(function () { + +var modules = {}, paths = []; // Base setup // ============================================================================= // Don't do anything if require is already there -if (typeof global.require === "function") { return; } +if (typeof require === "function") { return; } // Global exports object -if (typeof global.exports === "undefined") { global.exports = {}; } +if (typeof exports === "undefined") { exports = {}; } + +// Stand-in for require +require = function (id) { return modules[id]; }; + +// System +// ============================================================================= +modules.system = (function (exports) { + +var platform; + +exports.engine = "jscript"; +exports.os = "windows"; // WScript or ASP? if (typeof Response === "object" && typeof Response.write !== "undefined") { platform = "asp"; } else if (typeof WScript === "object") { platform = "wscript"; } else { platform = "unknown"; } - -// alias for Server.mapPath on ASP -function m(path) { return platform === "asp" ? Server.mapPath(path) : path; } +exports.platform = platform; // print function -function print() { +exports.print = function () { var out = Array.prototype.slice.call(arguments).join(" "); if (platform === "wscript") { WScript.echo(out); } else if (platform === "asp") { Response.write(out + "
"); } -} +}; -// Filesystem -// ============================================================================= +return exports; +})({}); -fs = { - // Filesystem object - o: new ActiveXObject("Scripting.FileSystemObject") +// File +// ============================================================================= +modules.file = (function (exports) { -}; +return exports; +})({}); // Loader // ============================================================================= @@ -52,10 +65,10 @@ function Loader() { // ============================================================================= function Sandbox() { - return function () {}; + return require; } -// ============================================================================ -global.require = Sandbox({ loader: Loader({ paths: paths }) }); +// ============================================================================= +require = Sandbox({ loader: Loader({ paths: paths }) }); })(this); diff --git a/test/test.js b/test/test.js index 26d619e..98d3224 100644 --- a/test/test.js +++ b/test/test.js @@ -1,7 +1,6 @@ // commonjscript test runner -require.paths.push("../lib"); // to get system module - +/* var print = require("system").print, modulesSpecVersion = "1.0"; tests = ["absolute", @@ -15,13 +14,18 @@ var print = require("system").print, "nested", "relative", "transitive"]; +*/ -function run(test) { - require.paths = ["commonjs/tests/modules/" + modulesSpecVersion + "/" + - test]; - print("-- " + test + "--") - require("program"); - print(""); -} +//function run(test) { + //require.paths = ["commonjs/tests/modules/" + modulesSpecVersion + "/" + + //test]; + //print("-- " + test + "--") + //require("program"); + //print(""); +//} -for (var i = 0; i < tests.length; i += 1) { run(tests[i]); } +//for (var i = 0; i < tests.length; i += 1) { run(tests[i]); } +//var print = require("system").print; +var print = require("system").print; +print("hi"); +//WScript.echo(typeof require("system").print); From 5e94fc865841007a7a20c31a87041287895d570a Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Wed, 5 May 2010 16:59:40 -0500 Subject: [PATCH 03/14] narwhal loader/sandbox & some file module --- require.js | 620 +++++++++++++++++++++++++++++++++++++++++++++++++-- test/test.js | 24 +- 2 files changed, 609 insertions(+), 35 deletions(-) diff --git a/require.js b/require.js index b4a3712..b9d055d 100644 --- a/require.js +++ b/require.js @@ -3,32 +3,27 @@ /*global require, exports, ActiveXObject, Response, Server, WScript */ /*jslint evil:true */ -// Globals - -(function () { - -var modules = {}, paths = []; +(function (global) { // Base setup // ============================================================================= +var modules = {}, paths = [], print; // Don't do anything if require is already there if (typeof require === "function") { return; } -// Global exports object -if (typeof exports === "undefined") { exports = {}; } - // Stand-in for require require = function (id) { return modules[id]; }; // System // ============================================================================= -modules.system = (function (exports) { +(function (exports) { var platform; exports.engine = "jscript"; exports.os = "windows"; +exports.debug = require.debug; // WScript or ASP? if (typeof Response === "object" && typeof Response.write !== "undefined") { @@ -37,38 +32,621 @@ if (typeof Response === "object" && typeof Response.write !== "undefined") { } else { platform = "unknown"; } exports.platform = platform; -// print function exports.print = function () { var out = Array.prototype.slice.call(arguments).join(" "); if (platform === "wscript") { WScript.echo(out); } else if (platform === "asp") { Response.write(out + "
"); } }; -return exports; -})({}); +exports.evaluate = function (text) { + // JScript's eval is not ES3 compliant, so the function needs to be + // assigned to a variable. + // @see http://www.bigresource.com/ASP-JScript-eval-bug-6nZST3Bk.html + var result; + eval("result = function (require, exports, module) {" + text + "/**/\n};"); + return result; +}; + +})(modules.system = {}); + +print = require("system").print; // Add print function here // File // ============================================================================= -modules.file = (function (exports) { +(function (exports) { + +var fso = new ActiveXObject("Scripting.FileSystemObject"), + stream = new ActiveXObject("ADODB.Stream"); + +// FIXME +exports.dirname = function (path) { + //return fso.getFile(path).path; + return path; +}; + +// For Windows API functions that manipulate files, file names can often be +// relative to the current directory, while some APIs require a fully qualified +// path. A file name is relative to the current directory if it does not begin +// with one of the following: +// +// * A UNC name of any format, which always start with two backslash characters +// ("\\"). For more information, see the next section. +// * A disk designator with a backslash, for example "C:\" or "d:\". +// * A single backslash, for example, "\directory" or "\file.txt". This is also +// referred to as an absolute path. +// +// @see http://stackoverflow.com/questions/2406739/how-to-find-whether-a-given-path-is-absolute-relative-and-convert-it-to-absolute +// @see http://msdn.microsoft.com/en-us/library/aa365247.aspx#fully_qualified_vs._relative_paths +exports.isAbsolute = function (path) { + return (/^(?:[A-Za-z]:)?\\/).test(path); +}; + +exports.isFile = function (path) { + return fso.fileExists(path); +}; + +exports.join = function () { + return Array.prototype.join.call(arguments, "\\"); +}; + +exports.mtime = function (path) { + return fso.getFile(path).dateLastModified; +} + +// TODO +exports.normal = function (path) { + return path; +}; + +exports.read = function (path, options) { + var charset = (options || {}).charset || "utf-8", + adTypeText = 2, + text = ""; + + stream.open(); + stream.charset = charset; + stream.type = adTypeText; + stream.loadFromFile(path); + text = stream.readText(); + stream.close(); + + return text; +}; -return exports; -})({}); +exports.Path = function (path) { this.valueOf = function () { return this; }; }; + +})(modules.file = {}); // Loader // ============================================================================= +(function (exports) { +// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License +// -- cadorn Christoph Dorn -function Loader() { +// NOTE: this file is used is the bootstrapping process, +// so any "requires" must be accounted for in narwhal.js -} +var system = require("system"); +// HACK: the stars prevent the file module from being sent to browser +// clients with the regexen we're using. we need a real solution +// for this. +var file = require(/**/"file"/**/); + +// this gets swapped out with a full fledged-read before +// we're done using it +var read = file.read; +var Module = system.Module || system.evaluate; // legacy + +exports.Loader = function (options) { + var loader = {}; + var factories = options.factories || {}; + var paths = options.paths; + var extensions = options.extensions || ["", ".js"]; + var timestamps = {}; + var debug = options.debug; + + loader.resolve = exports.resolve; + + loader.resolvePkg = function(id, baseId, pkg, basePkg) { + return exports.resolvePkg(loader, id, baseId, pkg, basePkg); + }; + + loader.find = function (topId) { + // if it's absolute only search the "root" directory. + // file.join() must collapse multiple "/" into a single "/" + var searchPaths = file.isAbsolute(topId) ? [""] : paths; + for (var j = 0; j < extensions.length; j++) { + var extension = extensions[j]; + for (var i = 0; i < searchPaths.length; i++) { + var path = file.join(searchPaths[i], topId + extension); + if (file.isFile(path)) + return path; + } + } + throw new Error("require error: couldn't find \"" + topId + '"'); + }; + + loader.fetch = function (topId, path) { + if (!path) + path = loader.find(topId); + if (typeof file.mtime === "function") + timestamps[path] = file.mtime(path); + if (debug) + print('loader: fetching ' + topId); + var text = read(path, { + 'charset': 'utf-8' + }); + // we leave the endline so the error line numbers align + text = text.replace(/^#[^\n]+\n/, "\n"); + return text; + }; + + loader.Module = function (text, topId, path) { + if (system.evaluate) { + if (!path) + path = loader.find(topId); + var factory = Module(text, path, 1); + factory.path = path; + return factory; + } else { + return function (inject) { + var keys = [], values = []; + for (var key in inject) { + if (Object.prototype.hasOwnProperty.call(inject, key)) { + keys.push(key); + values.push(inject[key]); + } + } + return Function.apply(null, keys).apply(this, values); + }; + } + }; + + loader.load = function (topId, path) { + if (!Object.prototype.hasOwnProperty.call(factories, topId)) { + loader.reload(topId, path); + } else if (typeof file.mtime === "function") { + var path = loader.find(topId); + if (loader.hasChanged(topId, path)) + loader.reload(topId, path); + } + return factories[topId]; + }; + + loader.reload = function (topId, path) { + factories[topId] = loader.Module(loader.fetch(topId, path), topId, path); + }; + + loader.isLoaded = function (topId) { + return Object.prototype.hasOwnProperty.call(factories, topId); + }; + + loader.hasChanged = function (topId, path) { + if (!path) + path = loader.resolve(topId); + return ( + !Object.prototype.hasOwnProperty.call(timestamps, path) || + file.mtime(path) > timestamps[path] + ); + }; + + loader.paths = paths; + loader.extensions = extensions; + + return loader; +}; + +exports.resolve = function (id, baseId) { + id = String(id); + if (id.charAt(0) == ".") { + id = file.dirname(baseId) + "/" + id; + } + // module ids need to use forward slashes, despite what the OS might say + return file.normal(id).replace(/\\/g, '/'); +}; + +exports.resolvePkg = function(loader, id, baseId, pkg, basePkg) { + if(!loader.usingCatalog) { + // no usingCatalog - fall back to default + return [exports.resolve(id, baseId), null]; + } + if(pkg) { + // locate id in pkg + if(basePkg && loader.usingCatalog[basePkg]) { + // see if pkg is an alias + var packages = loader.usingCatalog[basePkg].packages; + if(packages[pkg]) { + if(loader.usingCatalog[packages[pkg]]) { + var path = loader.usingCatalog[packages[pkg]].libPath; + return [exports.resolve("./" + id, path + "/"), packages[pkg]]; + } else { + throw "Package '"+packages[pkg]+"' aliased with '"+pkg+"' in '"+basePkg+"' not found"; + } + } + } + // see if pkg is a top-level ID + if(loader.usingCatalog[pkg]) { + var path = loader.usingCatalog[pkg].libPath; + return [exports.resolve("./" + id, path + "/"), pkg]; + } else { + throw "Package '" + pkg + "' not aliased in '"+basePkg+"' nor a top-level ID"; + } + } else { + // if id is relative we want a module relative to basePkg if it exists + if(id.charAt(0) == "." && basePkg) { + // if baseId is absolute we use it as a base and ignore basePkg + if(file.isAbsolute(baseId)) { + path = file.Path(baseId); + } else + if(loader.usingCatalog[basePkg]) { + path = loader.usingCatalog[basePkg].libPath.join(baseId); + } else { + throw "basePkg '" + basePkg + "' not known"; + } + + // try and locate the path - at this stage it should be found + return [exports.resolve(id, path.valueOf()), basePkg]; + + } else { + // id is not relative - resolve against system modules + return [exports.resolve(id, baseId), undefined]; + } + } +}; +})(modules.loader = {}); // Sandbox // ============================================================================= +(function (exports) { +// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License -function Sandbox() { - return require; -} +// NOTE: this file is used is the bootstrapping process, +// so any "requires" must be accounted for in narwhal.js + +var system = require("system"); + +exports.Sandbox = function (options) { + options = options || {}; + var loader = options.loader; + var subsystem = options.system || system || {}; + var exportsMemo = options.modules || {}; + var moduleMemo = {}; + var debug = options.debug !== undefined ? !!options.debug : !!system.debug; + var debugDepth = 0; + var main; + var setDisplayName = (system.engine == "jsc"); + + // managed print free variable in the sandbox forwards + // to system.print in the sandbox + var subprint = options.print || function () { + return subsystem.print.apply(subsystem, arguments); + }; + + var sandbox = function (id, baseId, pkg, basePkg, options) { + + if (!options) + options = {}; + + if (sandbox.debug) + print("REQUIRE: id["+id+"] baseId["+baseId+"] pkg["+pkg+"] basePkg["+basePkg+"]"); + + if (loader.resolvePkg) { + var resolveInfo = loader.resolvePkg(id, baseId, pkg, basePkg); + id = resolveInfo[0]; + pkg = resolveInfo[1]; + } else { + id = loader.resolve(id, baseId); + } + + if (sandbox.debug) + print("USING: id["+id+"] pkg["+pkg+"]"); + + /* populate memo with module instance */ + if (!Object.prototype.hasOwnProperty.call(exportsMemo, id) || options.force || options.once) { + + if (sandbox.debug) + print(new Array(++debugDepth + 1).join("\\") + " " + id, 'module'); + + var globals = {}; + if (sandbox.debug) { + // record globals + for (var name in global) + globals[name] = true; + } + + var exports; + if (options.once) { + exports = {}; + } else { + if (!Object.prototype.hasOwnProperty.call(exportsMemo, id) || options.reload) + exports = exportsMemo[id] = {}; + exports = exportsMemo[id]; + } + + if (options.reload) + loader.reload(id); + + var factory; + try { + factory = loader.load(id); + } finally { + // poor man's catch and rethrow (Rhino sets file/line to where the exception is thrown, not created) + if (!factory) { + delete exportsMemo[id]; + if (sandbox.debug) + debugDepth--; + } + } + + var require = Require(id, pkg); + var load = Load(id, pkg); + var module + = moduleMemo[id] + = moduleMemo[id] || Module(id, factory.path); + if (pkg) { + module["package"] = pkg; + module.using = ( + pkg && loader.usingCatalog && loader.usingCatalog[pkg] ? + loader.usingCatalog[pkg]["packages"] : + {} + ); + } + + // this shim supports both the old factory(r, e, m, s, p) and + // new factory(scope) module constructor conventions + var scope = require; + + // require.once provides a scope of extra stuff to inject + if (options.scope) { + for (var name in options.scope) { + if (Object.prototype.hasOwnProperty.call(options.scope, name)) { + scope[name] = options.scope[name]; + } + } + } + + scope.load = load; + scope.require = require; + scope.exports = exports; + scope.module = module; + scope.system = subsystem; + scope.print = subprint; + + var completed; + try { + factory(scope, exports, module, subsystem, subprint); + completed = true; + } finally { + if (!completed) { + delete exportsMemo[id]; + delete moduleMemo[id]; + } + } + + /* + // XXX to be uncommented when the above shim is + // no longer needed for migration + var scope = options.scope || {}; + scope.load = load; + scope.require = require; + scope.exports = exports; + scope.module = module; + scope.system = subsystem; + scope.print = subprint; + factory(scope); + */ + + if (sandbox.debug) { + // check for new globals + for (var name in global) + if (!globals[name]) + system.print("NEW GLOBAL: " + name); + } + + if (sandbox.debug) + print(new Array(debugDepth-- + 1).join("/") + " " + id, 'module'); + + // set fn.displayName on exported functions for better debugging + if (setDisplayName) { + var displayID = id.replace(/[^\w]/g, "_").toUpperCase(); + for (var name in exports) { + if (typeof exports[name] === "function" && !exports[name].displayName && + Object.prototype.hasOwnProperty.call(exports, name)) { + exports[name].displayName = displayID+"."+name; + } + } + } + + } else { + if (sandbox.debug > 1) + print(new Array(debugDepth + 1).join("|") + " " + id, 'module'); + exports = exportsMemo[id]; + if (moduleMemo[id]) { + moduleMemo[id].setExports = function () { + throw new Error("Cannot set exports after a module has been required by another module."); + }; + } + } + + /* support curryId for modules in which it is requested */ + var imports = {}; + var importsUsed = false; + var curryUsed = false; + for (var name in exports) { + curryUsed = ( + typeof exports[name] == "function" && + // if it is Java class this will throw an exception, which is terribly annoying during debugging + Object.prototype.toString.call(exports[name]) !== "[object JavaClass]" && + exports[name].xNarwhalCurry + ); + + if (curryUsed) { + importsUsed = true; + imports[name] = (function (block, module) { + return function () { + return block.apply( + this, + [module].concat(Array.prototype.slice.call(arguments)) + ); + }; + })(exports[name], moduleMemo[baseId]); + } else { + imports[name] = exports[name]; + } + } + + if (!importsUsed) + imports = exports; + + return imports; + + }; + + /* + sandbox.async = function (id, baseId, pkg, basePkg, options) { + }; + */ + + sandbox.load = function (id, baseId, pkg, basePkg) { + if (loader.resolvePkg) { + var resolveInfo = loader.resolvePkg(id, baseId, pkg, basePkg); + id = resolveInfo[0]; + pkg = resolveInfo[1]; + } else { + id = loader.resolve(id, baseId); + } + return loader.load(id); + }; + + sandbox.once = function (id, baseId, pkg, basePkg, scope) { + return sandbox(id, baseId, pkg, basePkg, {"scope": scope}); + }; + + /* + sandbox.load.async = function (id, baseId, pkg, basePkg, options) { + }; + */ + + sandbox.force = function (id) { + /* baseId, pkgId, basePkg , options */ + return sandbox(id, undefined, undefined, undefined, {"force": true}); + }; + + sandbox.main = function (id, path) { + if (!path && sandbox.loader.find) + path = sandbox.loader.find(id)[1]; + id = sandbox.loader.resolve(id, ""); + main = sandbox.main = moduleMemo[id] = moduleMemo[id] || Module(id, path); + sandbox(id); + return main; + }; + + sandbox.loader = loader; + sandbox.system = system; + sandbox.paths = loader.paths; + sandbox.extensions = loader.extensions; + sandbox.debug = debug; + + var Require = function (baseId, basePkg) { + // variations of require.* functions that close on a + // particular [package/]module + var require = function (id, pkg) { + return sandbox(id, baseId, pkg, basePkg); + }; + require.async = function (id, pkg) { + return sandbox.async(id, baseId, pkg, basePkg); + }; + require.once = function (id, scope) { + return sandbox.once(id, baseId, undefined, undefined, scope); + }; + require.once.async = function (id, scope) { + return sandbox.once.async(id, baseId, undefined, undefined, scope); + }; + require.load = function (id, pkg) { + return sandbox.load(id, baseId, pkg, basePkg); + }; + require.load.async = function (id, pkg) { + return sandbox.load.async(id, baseId, pkg, basePkg); + }; + require.force = function (id) { + return sandbox.force(id, baseId); + }; + require.loader = loader; + require.main = main; + require.paths = loader.paths; + require.extensions = loader.extensions; + return require; + }; + + var Load = function (baseId, basePkg) { + var load = function (id, pkg) { + return sandbox.load(id, baseId, pkg, basePkg); + }; + load.async = function (id) { + return sandbox.load.async(id, baseId, pkg, basePkg); + }; + return load; + }; + + var Module = function (baseId, path) { + var module = {}; + module.id = baseId; + module.path = path; + module.toString = function () { + return baseId; + }; + module.xNarwhalCurry = function (block) { + block.xNarwhalCurry = true; + return block; + }; + module.setExports = function (exports) { + return exportsMemo[baseId] = exports; + }; + return module; + }; + + return sandbox; +}; + +exports.sandbox = function(main, system, options) { + options = options || {}; + var prefix = options['prefix']; + var loader = options['loader'] || require.loader; + var modules = options['modules'] || {}; + var print = options['print']; + var debug = options['debug']; + if (!loader) throw new Error( + "sandbox cannot operate without a loader, either explicitly " + + "provided as an option, or implicitly provided by the current " + + "sandbox's 'loader' object." + ); + if (prefix) + loader = require("loader/prefix").PrefixLoader(prefix, loader); + var sandbox = exports.Sandbox({ + modules: modules, + loader: loader, + system: system, + print: print, + debug: debug + }); + + return sandbox.main(main); +}; +})(modules.sandbox = {}); // ============================================================================= -require = Sandbox({ loader: Loader({ paths: paths }) }); + +// Set up paths +require.paths = [".", "lib", "engines/" + require("system").engine + "/lib"]; + +// Create require +require = require("sandbox").Sandbox({ + loader: require("loader").Loader({ + paths: require.paths, + debug: require.debug + }), + modules: modules, + debug: require.debug +}); })(this); diff --git a/test/test.js b/test/test.js index 98d3224..26d619e 100644 --- a/test/test.js +++ b/test/test.js @@ -1,6 +1,7 @@ // commonjscript test runner -/* +require.paths.push("../lib"); // to get system module + var print = require("system").print, modulesSpecVersion = "1.0"; tests = ["absolute", @@ -14,18 +15,13 @@ var print = require("system").print, "nested", "relative", "transitive"]; -*/ -//function run(test) { - //require.paths = ["commonjs/tests/modules/" + modulesSpecVersion + "/" + - //test]; - //print("-- " + test + "--") - //require("program"); - //print(""); -//} +function run(test) { + require.paths = ["commonjs/tests/modules/" + modulesSpecVersion + "/" + + test]; + print("-- " + test + "--") + require("program"); + print(""); +} -//for (var i = 0; i < tests.length; i += 1) { run(tests[i]); } -//var print = require("system").print; -var print = require("system").print; -print("hi"); -//WScript.echo(typeof require("system").print); +for (var i = 0; i < tests.length; i += 1) { run(tests[i]); } From 4d8a7d5718953c8c928a9c96ad02a0f32a462a79 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Fri, 7 May 2010 14:59:03 -0500 Subject: [PATCH 04/14] path mapping & start of error handling --- require.js | 53 +++++++++++++++++++++++-------------- test/asp/absolute.asp | 7 +++++ test/asp/cyclic.asp | 7 +++++ test/asp/determinism.asp | 7 +++++ test/asp/exactExports.asp | 7 +++++ test/asp/hasOwnProperty.asp | 7 +++++ test/asp/index.asp | 19 +++++++++++++ test/asp/method.asp | 7 +++++ test/asp/missing.asp | 7 +++++ test/asp/monkeys.asp | 7 +++++ test/asp/nested.asp | 7 +++++ test/asp/relative.asp | 7 +++++ test/asp/transitive.asp | 7 +++++ test/test.asp | 2 -- test/test.js | 27 ------------------- 15 files changed, 129 insertions(+), 49 deletions(-) create mode 100644 test/asp/absolute.asp create mode 100644 test/asp/cyclic.asp create mode 100644 test/asp/determinism.asp create mode 100644 test/asp/exactExports.asp create mode 100644 test/asp/hasOwnProperty.asp create mode 100644 test/asp/index.asp create mode 100644 test/asp/method.asp create mode 100644 test/asp/missing.asp create mode 100644 test/asp/monkeys.asp create mode 100644 test/asp/nested.asp create mode 100644 test/asp/relative.asp create mode 100644 test/asp/transitive.asp delete mode 100644 test/test.asp delete mode 100644 test/test.js diff --git a/require.js b/require.js index b9d055d..af8e2fd 100644 --- a/require.js +++ b/require.js @@ -42,8 +42,14 @@ exports.evaluate = function (text) { // JScript's eval is not ES3 compliant, so the function needs to be // assigned to a variable. // @see http://www.bigresource.com/ASP-JScript-eval-bug-6nZST3Bk.html - var result; - eval("result = function (require, exports, module) {" + text + "/**/\n};"); + var result, ee; + eval("result = function (require, exports, module) { try {" + text + + // Fancy error augmentation surgery + "} catch (e) {" + + "throw new e.constructor(e.description + ' (in module: ' + module.id + ')');" + + "}};" + ); + return result; }; @@ -56,7 +62,13 @@ print = require("system").print; // Add print function here (function (exports) { var fso = new ActiveXObject("Scripting.FileSystemObject"), - stream = new ActiveXObject("ADODB.Stream"); + stream = new ActiveXObject("ADODB.Stream"), + platform = require("system").platform; + +// Wrap all calls in Server.mapPath if we're on ASP +function m(path) { + return platform === "asp" ? Server.mapPath(path) : path; +} // FIXME exports.dirname = function (path) { @@ -82,7 +94,7 @@ exports.isAbsolute = function (path) { }; exports.isFile = function (path) { - return fso.fileExists(path); + return fso.fileExists(m(path)); }; exports.join = function () { @@ -90,7 +102,7 @@ exports.join = function () { }; exports.mtime = function (path) { - return fso.getFile(path).dateLastModified; + return fso.getFile(m(path)).dateLastModified; } // TODO @@ -106,14 +118,17 @@ exports.read = function (path, options) { stream.open(); stream.charset = charset; stream.type = adTypeText; - stream.loadFromFile(path); + stream.loadFromFile(m(path)); text = stream.readText(); stream.close(); return text; }; -exports.Path = function (path) { this.valueOf = function () { return this; }; }; +exports.Path = function (path) { + this.path = m(path); + this.valueOf = function () { return this.path; }; +}; })(modules.file = {}); @@ -348,11 +363,11 @@ exports.Sandbox = function (options) { print(new Array(++debugDepth + 1).join("\\") + " " + id, 'module'); var globals = {}; - if (sandbox.debug) { + //if (sandbox.debug) { // record globals - for (var name in global) - globals[name] = true; - } + //for (var name in global) + //globals[name] = true; + //} var exports; if (options.once) { @@ -436,12 +451,12 @@ exports.Sandbox = function (options) { factory(scope); */ - if (sandbox.debug) { + //if (sandbox.debug) { // check for new globals - for (var name in global) - if (!globals[name]) - system.print("NEW GLOBAL: " + name); - } + //for (var name in global) + //if (!globals[name]) + //system.print("NEW GLOBAL: " + name); + //} if (sandbox.debug) print(new Array(debugDepth-- + 1).join("/") + " " + id, 'module'); @@ -637,13 +652,11 @@ exports.sandbox = function(main, system, options) { // ============================================================================= // Set up paths -require.paths = [".", "lib", "engines/" + require("system").engine + "/lib"]; +paths = ["lib", "engines/" + require("system").engine + "/lib"]; // Create require require = require("sandbox").Sandbox({ - loader: require("loader").Loader({ - paths: require.paths, - debug: require.debug + loader: require("loader").Loader({ paths: paths, debug: require.debug }), modules: modules, debug: require.debug diff --git a/test/asp/absolute.asp b/test/asp/absolute.asp new file mode 100644 index 0000000..36fe802 --- /dev/null +++ b/test/asp/absolute.asp @@ -0,0 +1,7 @@ + + diff --git a/test/asp/cyclic.asp b/test/asp/cyclic.asp new file mode 100644 index 0000000..d242915 --- /dev/null +++ b/test/asp/cyclic.asp @@ -0,0 +1,7 @@ + + diff --git a/test/asp/determinism.asp b/test/asp/determinism.asp new file mode 100644 index 0000000..61491b2 --- /dev/null +++ b/test/asp/determinism.asp @@ -0,0 +1,7 @@ + + diff --git a/test/asp/exactExports.asp b/test/asp/exactExports.asp new file mode 100644 index 0000000..a21d894 --- /dev/null +++ b/test/asp/exactExports.asp @@ -0,0 +1,7 @@ + + diff --git a/test/asp/hasOwnProperty.asp b/test/asp/hasOwnProperty.asp new file mode 100644 index 0000000..525d986 --- /dev/null +++ b/test/asp/hasOwnProperty.asp @@ -0,0 +1,7 @@ + + diff --git a/test/asp/index.asp b/test/asp/index.asp new file mode 100644 index 0000000..c347f92 --- /dev/null +++ b/test/asp/index.asp @@ -0,0 +1,19 @@ + diff --git a/test/asp/method.asp b/test/asp/method.asp new file mode 100644 index 0000000..83ffc0a --- /dev/null +++ b/test/asp/method.asp @@ -0,0 +1,7 @@ + + diff --git a/test/asp/missing.asp b/test/asp/missing.asp new file mode 100644 index 0000000..12f350b --- /dev/null +++ b/test/asp/missing.asp @@ -0,0 +1,7 @@ + + diff --git a/test/asp/monkeys.asp b/test/asp/monkeys.asp new file mode 100644 index 0000000..b1c9d0f --- /dev/null +++ b/test/asp/monkeys.asp @@ -0,0 +1,7 @@ + + diff --git a/test/asp/nested.asp b/test/asp/nested.asp new file mode 100644 index 0000000..186f413 --- /dev/null +++ b/test/asp/nested.asp @@ -0,0 +1,7 @@ + + diff --git a/test/asp/relative.asp b/test/asp/relative.asp new file mode 100644 index 0000000..6ba6c11 --- /dev/null +++ b/test/asp/relative.asp @@ -0,0 +1,7 @@ + + diff --git a/test/asp/transitive.asp b/test/asp/transitive.asp new file mode 100644 index 0000000..671a271 --- /dev/null +++ b/test/asp/transitive.asp @@ -0,0 +1,7 @@ + + diff --git a/test/test.asp b/test/test.asp deleted file mode 100644 index 37b6332..0000000 --- a/test/test.asp +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/test/test.js b/test/test.js deleted file mode 100644 index 26d619e..0000000 --- a/test/test.js +++ /dev/null @@ -1,27 +0,0 @@ -// commonjscript test runner - -require.paths.push("../lib"); // to get system module - -var print = require("system").print, - modulesSpecVersion = "1.0"; - tests = ["absolute", - "cyclic", - "determinism", - "exactExports", - "hasOwnProperty", - "method", - "missing", - "monkeys", - "nested", - "relative", - "transitive"]; - -function run(test) { - require.paths = ["commonjs/tests/modules/" + modulesSpecVersion + "/" + - test]; - print("-- " + test + "--") - require("program"); - print(""); -} - -for (var i = 0; i < tests.length; i += 1) { run(tests[i]); } From 03f75d39cabab8570b03d099e7ecf8589834bfdf Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Sat, 8 May 2010 15:38:40 -0500 Subject: [PATCH 05/14] 1st non-working try --- require.js | 576 +++++++++++++++++++++++++++++++++++++------------- test/test.wsf | 4 - 2 files changed, 425 insertions(+), 155 deletions(-) delete mode 100644 test/test.wsf diff --git a/require.js b/require.js index af8e2fd..fafb953 100644 --- a/require.js +++ b/require.js @@ -1,119 +1,414 @@ -// commonjscript require +// CommonJScript for ASP -/*global require, exports, ActiveXObject, Response, Server, WScript */ +/*global require, exports, ActiveXObject, Response, Server */ /*jslint evil:true */ -(function (global) { +(function () { // Base setup // ============================================================================= -var modules = {}, paths = [], print; +var modules = {}, paths = [], print, global; // Don't do anything if require is already there if (typeof require === "function") { return; } +// ASP Only +if (typeof Server === "undefined" || typeof Response === "undefined") { + throw Error("This script only runs in ASP."); +} + // Stand-in for require -require = function (id) { return modules[id]; }; +require = function (id) { return modules[id]; }; // global -// System +// JScript does not support assigning properties explicitly on the global +// object or a reference to it. Some included modules try to modify this, so +// use an empty object in its place. +global = {}; + +// Engine // ============================================================================= (function (exports) { -var platform; - exports.engine = "jscript"; -exports.os = "windows"; -exports.debug = require.debug; - -// WScript or ASP? -if (typeof Response === "object" && typeof Response.write !== "undefined") { - platform = "asp"; -} else if (typeof WScript === "object") { platform = "wscript"; -} else { platform = "unknown"; } -exports.platform = platform; - -exports.print = function () { - var out = Array.prototype.slice.call(arguments).join(" "); - if (platform === "wscript") { WScript.echo(out); } - else if (platform === "asp") { Response.write(out + "
"); } +exports.debug = "require.debug"; + +// We're treating paths on ASP like Unix paths, so misreport the os. +// Hey, IE's been calling itself Mozilla all these years, so this is nothing. +exports.os = "asp"; + +exports.Module = function (text, path, line) { + // Return a function that takes an object, which returns another function + // that takes the inject properties + return function (inject) { + inject = inject || {}; + var names = [], result; + + for (var name in inject) { + if (Object.prototype.hasOwnProperty.call(inject, name)) { + names.push(name); + } + } + + // JScript's eval is not ES3 compliant, so the function needs to be + // assigned to a variable. + // @see http://www.bigresource.com/ASP-JScript-eval-bug-6nZST3Bk.html + eval("result = function (" + names.join(", ") + ") { " + text + " };"); + //eval("result = function (require, exports, module) { try {" + text + + // Fancy error augmentation surgery + //"} catch (e) {" + + //"throw new e.constructor(e.description + ' (in module: ' + path + ')');" + + //"}};" + //); + return result; + }; }; -exports.evaluate = function (text) { - // JScript's eval is not ES3 compliant, so the function needs to be - // assigned to a variable. - // @see http://www.bigresource.com/ASP-JScript-eval-bug-6nZST3Bk.html - var result, ee; - eval("result = function (require, exports, module) { try {" + text + - // Fancy error augmentation surgery - "} catch (e) {" + - "throw new e.constructor(e.description + ' (in module: ' + module.id + ')');" + - "}};" - ); +})(modules.engine = {}); - return result; +// System +// ============================================================================= +(function (exports) { + +exports.platform = "asp"; + +exports.stdout = { + print: function () { + Response.write(Array.prototype.slice.call(arguments).join(" ") + + "
"); + } }; +// system.stdio.print is not defined in any specs, but the Modules/1.0 test +// suite uses it +exports.stdio = { print: exports.stdout.print }; + +exports.print = exports.stdout.print; + +// TODO +exports.env = {}; + })(modules.system = {}); print = require("system").print; // Add print function here // File +// +// From narwhal-lib/lib/narwhal/fs-boot.js // ============================================================================= (function (exports) { +// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License +// -- tlrobinson Tom Robinson TODO + +/** + * Pure JavaScript implementations of file system path + * manipulation. + * + * This module depends on the non CommonJS "engine" module, + * particularly for an "os" property that has the words + * "windows" or "winnt" to distinguish Windows from Unix + * file systems. + */ + +// NOTE: this file may be used is the engine bootstrapping +// process, so any "requires" must be accounted for in +// narwhal.js + +/*whatsupdoc*/ +/*markup markdown*/ + +var ENGINE = require("engine"); + +/** + * @name ROOT + * * `/` on Unix + * * `\` on Windows + */ + +/** + * @name SEPARATOR + * * `/` on Unix + * * `\` on Windows + */ + +/** + * @name ALT_SEPARATOR + * * undefined on Unix + * * `/` on Windows + */ + +if (/\bwind(nt|ows)\b/i.test(ENGINE.os)) { + exports.ROOT = "\\"; + exports.SEPARATOR = "\\"; + exports.ALT_SEPARATOR = "/"; +} else { + exports.ROOT = "/"; + exports.SEPARATOR = "/"; + exports.ALT_SEPARATOR = undefined; +} -var fso = new ActiveXObject("Scripting.FileSystemObject"), - stream = new ActiveXObject("ADODB.Stream"), - platform = require("system").platform; - -// Wrap all calls in Server.mapPath if we're on ASP -function m(path) { - return platform === "asp" ? Server.mapPath(path) : path; +// we need to make sure the separator regex is always in sync with the separators. +// this caches the generated regex and rebuild if either separator changes. +var separatorCached, altSeparatorCached, separatorReCached; +/** + * @function + */ +exports.SEPARATORS_RE = function () { + if ( + separatorCached !== exports.SEPARATOR || + altSeparatorCached !== exports.ALT_SEPARATOR + ) { + separatorCached = exports.SEPARATOR; + altSeparatorCached = exports.ALT_SEPARATOR; + separatorReCached = new RegExp("[" + + (separatorCached || '').replace(/[-[\]{}()*+?.\\^$|,#\s]/g, "\\$&") + + (altSeparatorCached || '').replace(/[-[\]{}()*+?.\\^$|,#\s]/g, "\\$&") + + "]", "g"); + } + return separatorReCached; } -// FIXME -exports.dirname = function (path) { - //return fso.getFile(path).path; - return path; +/** + * separates a path into components. If the path is + * absolute, the first path component is the root of the + * file system, indicated by an empty string on Unix, and a + * drive letter followed by a colon on Windows. + * @returns {Array * String} + */ +exports.split = function (path) { + var parts; + try { + parts = String(path).split(exports.SEPARATORS_RE()); + } catch (exception) { + throw new Error("Cannot split " + (typeof path) + ', "' + path + '"'); + } + // this special case helps isAbsolute + // distinguish an empty path from an absolute path + // "" -> [] NOT [""] + if (parts.length == 1 && parts[0] == "") + return []; + // "a" -> ["a"] + // "/a" -> ["", "a"] + return parts; }; -// For Windows API functions that manipulate files, file names can often be -// relative to the current directory, while some APIs require a fully qualified -// path. A file name is relative to the current directory if it does not begin -// with one of the following: -// -// * A UNC name of any format, which always start with two backslash characters -// ("\\"). For more information, see the next section. -// * A disk designator with a backslash, for example "C:\" or "d:\". -// * A single backslash, for example, "\directory" or "\file.txt". This is also -// referred to as an absolute path. -// -// @see http://stackoverflow.com/questions/2406739/how-to-find-whether-a-given-path-is-absolute-relative-and-convert-it-to-absolute -// @see http://msdn.microsoft.com/en-us/library/aa365247.aspx#fully_qualified_vs._relative_paths +/** + * Takes file system paths as variadic arguments and treats + * each as a file or directory path and returns the path + * arrived by traversing into the those paths. All + * arguments except for the last must be paths to + * directories for the result to be meaningful. + * @returns {String} path + */ +exports.join = function () { + if (arguments.length === 1 && typeof arguments[0] === "object") + return exports.normal.apply(exports, arguments[0]); + return exports.normal.apply(exports, arguments); +}; + +/** + * Takes file system paths as variadic arguments and treats + * each path as a location, in the URL sense, resolving each + * new location based on the previous. For example, if the + * first argument is the absolute path of a JSON file, and + * the second argument is a path mentioned in that JSON + * file, `resolve` returns the absolute path of the + * mentioned file. + * @returns {String} path + */ +exports.resolve = function () { + var root = ""; + var parents = []; + var children = []; + var leaf = ""; + for (var i = 0; i < arguments.length; i++) { + var path = String(arguments[i]); + if (path == "") + continue; + var parts = path.split(exports.SEPARATORS_RE()); + if (exports.isAbsolute(path)) { + root = parts.shift() + exports.SEPARATOR; + parents = []; + children = []; + } + leaf = parts.pop(); + if (leaf == "." || leaf == "..") { + parts.push(leaf); + leaf = ""; + } + for (var j = 0; j < parts.length; j++) { + var part = parts[j]; + if (part == "." || part == '') { + } else if (part == "..") { + if (children.length) { + children.pop(); + } else { + if (root) { + } else { + parents.push(".."); + } + } + } else { + children.push(part); + } + }; + } + path = parents.concat(children).join(exports.SEPARATOR); + if (path) leaf = exports.SEPARATOR + leaf; + return root + path + leaf; +}; + +/** + * Takes paths as any number of arguments and reduces them + * into a single path in normal form, removing all "." path + * components, and reducing ".." path components by removing + * the previous path component if possible. + * @returns {String} path + */ +exports.normal = function () { + var root = ""; + var parents = []; + var children = []; + for (var i = 0, ii = arguments.length; i < ii; i++) { + var path = String(arguments[i]); + // empty paths have no affect + if (path === "") + continue; + var parts = path.split(exports.SEPARATORS_RE()); + if (exports.isAbsolute(path)) { + root = parts.shift() + exports.SEPARATOR; + parents = []; + children = []; + } + for (var j = 0, jj = parts.length; j < jj; j++) { + var part = parts[j]; + if (part == "." || part == '') { + } else if (part == "..") { + if (children.length) { + children.pop(); + } else { + if (root) { + } else { + parents.push(".."); + } + } + } else { + children.push(part); + } + } + } + path = parents.concat(children).join(exports.SEPARATOR); + return root + path; +}; + +/*** + * @returns {Boolean} whether the given path begins at the + * root of the file system or a drive letter. + */ exports.isAbsolute = function (path) { - return (/^(?:[A-Za-z]:)?\\/).test(path); + // for absolute paths on any operating system, + // the first path component always determines + // whether it is relative or absolute. On Unix, + // it is empty, so ['', 'foo'].join('/') == '/foo', + // '/foo'.split('/') == ['', 'foo']. + var parts = exports.split(path); + // split('') == []. '' is not absolute. + // split('/') == ['', ''] is absolute. + // split(?) == [''] does not occur. + if (parts.length == 0) + return false; + return exports.isRoot(parts[0]); }; -exports.isFile = function (path) { - return fso.fileExists(m(path)); +/** + * @returns {Boolean} whether the given path does not begin + * at the root of the file system or a drive letter. + */ +exports.isRelative = function (path) { + return !exports.isAbsolute(path); }; -exports.join = function () { - return Array.prototype.join.call(arguments, "\\"); +/** + * @returns {Boolean} whether the given path component + * corresponds to the root of the file system or a drive + * letter, as applicable. + */ +exports.isRoot = function (first) { + if (/\bwind(nt|ows)\b/i.test(ENGINE.os)) { + return /:$/.test(first); + } else { + return first == ""; + } }; -exports.mtime = function (path) { - return fso.getFile(m(path)).dateLastModified; -} +/** + * @returns {String} the Unix root path or corresponding + * Windows drive for a given path. + */ +exports.root = function (path) { + if (!exports.isAbsolute(path)) + path = require("./fs").absolute(path); + var parts = exports.split(path); + return exports.join(parts[0], ''); +}; -// TODO -exports.normal = function (path) { - return path; +/** + * @returns {String} the parent directory of the given path. + */ +exports.directory = function (path) { + var parts = exports.split(path); + // XXX needs to be sensitive to the root for + // Windows compatibility + parts.pop(); + return parts.join(exports.SEPARATOR) || "."; +}; + +/** + * @returns {String} the last component of a path, without + * the given extension if the extension is provided and + * matches the given file. + * @param {String} path + * @param {String} extention an optional extention to detect + * and remove if it exists. + */ +exports.base = function (path, extension) { + var base = path.split(exports.SEPARATORS_RE()).pop(); + if (extension) + base = base.replace( + new RegExp(RegExp.escape(extension) + '$'), + '' + ); + return base; +}; + +/** + * @returns {String} the extension (e.g., `txt`) of the file + * at the given path. + */ +exports.extension = function (path) { + path = exports.base(path); + path = path.replace(/^\.*/, ''); + var index = path.lastIndexOf("."); + return index <= 0 ? "" : path.substring(index); +}; +})(modules["narwhal/fs"] = {}); + +// File - platform specific +// ============================================================================= +(function (exports) { + +var fso = new ActiveXObject("Scripting.FileSystemObject"), + stream = new ActiveXObject("ADODB.Stream"); + +// Wrap some calls in Server.mapPath on ASP +function m(path) { return Server.mapPath(path) } + +exports.isFile = function (path) { + return fso.fileExists(m(path)); }; exports.read = function (path, options) { var charset = (options || {}).charset || "utf-8", - adTypeText = 2, - text = ""; + adTypeText = 2, text = ""; stream.open(); stream.charset = charset; @@ -124,33 +419,30 @@ exports.read = function (path, options) { return text; }; - -exports.Path = function (path) { - this.path = m(path); - this.valueOf = function () { return this.path; }; -}; - -})(modules.file = {}); +})(modules.file = modules["narwhal/fs"]); // Loader +// +// from narwhal-lib/lib/narwhal/loader.js // ============================================================================= (function (exports) { // -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License // -- cadorn Christoph Dorn -// NOTE: this file is used is the bootstrapping process, -// so any "requires" must be accounted for in narwhal.js +// NOTE: when this file is being loaded as part of the +// Narwhal bootstrapping process, all of the "requires" that +// occur here have to be manually accounted for (who loads +// the loader?) -var system = require("system"); +var ENGINE = require("engine"); // HACK: the stars prevent the file module from being sent to browser // clients with the regexen we're using. we need a real solution // for this. -var file = require(/**/"file"/**/); +var FS = require(/**/"narwhal/fs"/**/); // this gets swapped out with a full fledged-read before // we're done using it -var read = file.read; -var Module = system.Module || system.evaluate; // legacy +var read = FS.read; exports.Loader = function (options) { var loader = {}; @@ -168,13 +460,14 @@ exports.Loader = function (options) { loader.find = function (topId) { // if it's absolute only search the "root" directory. - // file.join() must collapse multiple "/" into a single "/" - var searchPaths = file.isAbsolute(topId) ? [""] : paths; + // FS.join() must collapse multiple "/" into a single "/" + var searchPaths = FS.isAbsolute(topId) ? [""] : paths; + for (var j = 0; j < extensions.length; j++) { var extension = extensions[j]; for (var i = 0; i < searchPaths.length; i++) { - var path = file.join(searchPaths[i], topId + extension); - if (file.isFile(path)) + var path = FS.join(searchPaths[i], topId + extension); + if (FS.isFile(path)) return path; } } @@ -184,8 +477,8 @@ exports.Loader = function (options) { loader.fetch = function (topId, path) { if (!path) path = loader.find(topId); - if (typeof file.mtime === "function") - timestamps[path] = file.mtime(path); + if (typeof FS.lastModified === "function") + timestamps[path] = FS.lastModified(path); if (debug) print('loader: fetching ' + topId); var text = read(path, { @@ -197,10 +490,10 @@ exports.Loader = function (options) { }; loader.Module = function (text, topId, path) { - if (system.evaluate) { + if (ENGINE.Module) { if (!path) path = loader.find(topId); - var factory = Module(text, path, 1); + var factory = ENGINE.Module(text, path, 1); factory.path = path; return factory; } else { @@ -220,7 +513,7 @@ exports.Loader = function (options) { loader.load = function (topId, path) { if (!Object.prototype.hasOwnProperty.call(factories, topId)) { loader.reload(topId, path); - } else if (typeof file.mtime === "function") { + } else if (typeof FS.lastModified === "function") { var path = loader.find(topId); if (loader.hasChanged(topId, path)) loader.reload(topId, path); @@ -241,7 +534,7 @@ exports.Loader = function (options) { path = loader.resolve(topId); return ( !Object.prototype.hasOwnProperty.call(timestamps, path) || - file.mtime(path) > timestamps[path] + FS.lastModified(path) > timestamps[path] ); }; @@ -254,10 +547,10 @@ exports.Loader = function (options) { exports.resolve = function (id, baseId) { id = String(id); if (id.charAt(0) == ".") { - id = file.dirname(baseId) + "/" + id; + id = FS.directory(baseId) + "/" + id; } // module ids need to use forward slashes, despite what the OS might say - return file.normal(id).replace(/\\/g, '/'); + return FS.normal(id).replace(/\\/g, '/'); }; exports.resolvePkg = function(loader, id, baseId, pkg, basePkg) { @@ -290,10 +583,9 @@ exports.resolvePkg = function(loader, id, baseId, pkg, basePkg) { // if id is relative we want a module relative to basePkg if it exists if(id.charAt(0) == "." && basePkg) { // if baseId is absolute we use it as a base and ignore basePkg - if(file.isAbsolute(baseId)) { - path = file.Path(baseId); - } else - if(loader.usingCatalog[basePkg]) { + if (FS.isAbsolute(baseId)) { + path = FS.Path(baseId); + } else if (loader.usingCatalog[basePkg]) { path = loader.usingCatalog[basePkg].libPath.join(baseId); } else { throw "basePkg '" + basePkg + "' not known"; @@ -311,6 +603,8 @@ exports.resolvePkg = function(loader, id, baseId, pkg, basePkg) { })(modules.loader = {}); // Sandbox +// +// from narwhal-lib/lib/narwhal/sandbox.js // ============================================================================= (function (exports) { // -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License @@ -318,22 +612,23 @@ exports.resolvePkg = function(loader, id, baseId, pkg, basePkg) { // NOTE: this file is used is the bootstrapping process, // so any "requires" must be accounted for in narwhal.js -var system = require("system"); +var SYSTEM = require("system"); +var ENGINE = require("engine"); exports.Sandbox = function (options) { options = options || {}; var loader = options.loader; - var subsystem = options.system || system || {}; var exportsMemo = options.modules || {}; var moduleMemo = {}; - var debug = options.debug !== undefined ? !!options.debug : !!system.debug; + var debug = options.debug !== undefined ? !!options.debug : !!ENGINE.debug; var debugDepth = 0; var main; - var setDisplayName = (system.engine == "jsc"); + var setDisplayName = (ENGINE.engine == "jsc"); // managed print free variable in the sandbox forwards // to system.print in the sandbox var subprint = options.print || function () { + var subsystem = sandbox("system"); return subsystem.print.apply(subsystem, arguments); }; @@ -343,7 +638,7 @@ exports.Sandbox = function (options) { options = {}; if (sandbox.debug) - print("REQUIRE: id["+id+"] baseId["+baseId+"] pkg["+pkg+"] basePkg["+basePkg+"]"); + SYSTEM.print("REQUIRE: id["+id+"] baseId["+baseId+"] pkg["+pkg+"] basePkg["+basePkg+"]"); if (loader.resolvePkg) { var resolveInfo = loader.resolvePkg(id, baseId, pkg, basePkg); @@ -354,20 +649,20 @@ exports.Sandbox = function (options) { } if (sandbox.debug) - print("USING: id["+id+"] pkg["+pkg+"]"); + SYSTEM.print("USING: id["+id+"] pkg["+pkg+"]"); /* populate memo with module instance */ if (!Object.prototype.hasOwnProperty.call(exportsMemo, id) || options.force || options.once) { if (sandbox.debug) - print(new Array(++debugDepth + 1).join("\\") + " " + id, 'module'); + SYSTEM.print(new Array(++debugDepth + 1).join("\\") + " " + id, 'module'); var globals = {}; - //if (sandbox.debug) { + if (sandbox.debug) { // record globals - //for (var name in global) - //globals[name] = true; - //} + for (var name in global) + globals[name] = true; + } var exports; if (options.once) { @@ -407,9 +702,12 @@ exports.Sandbox = function (options) { ); } - // this shim supports both the old factory(r, e, m, s, p) and - // new factory(scope) module constructor conventions - var scope = require; + var scope = { + "require": require, + "exports": exports, + "module": module, + "print": subprint + }; // require.once provides a scope of extra stuff to inject if (options.scope) { @@ -420,16 +718,9 @@ exports.Sandbox = function (options) { } } - scope.load = load; - scope.require = require; - scope.exports = exports; - scope.module = module; - scope.system = subsystem; - scope.print = subprint; - var completed; try { - factory(scope, exports, module, subsystem, subprint); + factory(scope); completed = true; } finally { if (!completed) { @@ -438,28 +729,15 @@ exports.Sandbox = function (options) { } } - /* - // XXX to be uncommented when the above shim is - // no longer needed for migration - var scope = options.scope || {}; - scope.load = load; - scope.require = require; - scope.exports = exports; - scope.module = module; - scope.system = subsystem; - scope.print = subprint; - factory(scope); - */ - - //if (sandbox.debug) { + if (sandbox.debug) { // check for new globals - //for (var name in global) - //if (!globals[name]) - //system.print("NEW GLOBAL: " + name); - //} + for (var name in global) + if (!globals[name]) + SYSTEM.print("NEW GLOBAL: " + name); + } if (sandbox.debug) - print(new Array(debugDepth-- + 1).join("/") + " " + id, 'module'); + SYSTEM.print(new Array(debugDepth-- + 1).join("/") + " " + id, 'module'); // set fn.displayName on exported functions for better debugging if (setDisplayName) { @@ -474,7 +752,7 @@ exports.Sandbox = function (options) { } else { if (sandbox.debug > 1) - print(new Array(debugDepth + 1).join("|") + " " + id, 'module'); + SYSTEM.print(new Array(debugDepth + 1).join("|") + " " + id, 'module'); exports = exportsMemo[id]; if (moduleMemo[id]) { moduleMemo[id].setExports = function () { @@ -557,7 +835,7 @@ exports.Sandbox = function (options) { }; sandbox.loader = loader; - sandbox.system = system; + sandbox.system = SYSTEM; sandbox.paths = loader.paths; sandbox.extensions = loader.extensions; sandbox.debug = debug; @@ -607,9 +885,6 @@ exports.Sandbox = function (options) { var module = {}; module.id = baseId; module.path = path; - module.toString = function () { - return baseId; - }; module.xNarwhalCurry = function (block) { block.xNarwhalCurry = true; return block; @@ -636,11 +911,10 @@ exports.sandbox = function(main, system, options) { "sandbox's 'loader' object." ); if (prefix) - loader = require("loader/prefix").PrefixLoader(prefix, loader); + loader = require("narwhal/loader/prefix").PrefixLoader(prefix, loader); var sandbox = exports.Sandbox({ modules: modules, loader: loader, - system: system, print: print, debug: debug }); @@ -652,12 +926,12 @@ exports.sandbox = function(main, system, options) { // ============================================================================= // Set up paths -paths = ["lib", "engines/" + require("system").engine + "/lib"]; +paths = ["", "lib", "engines/" + require("engine").engine + "/lib", + "engines/" + require("system").platform + "/lib"]; // Create require require = require("sandbox").Sandbox({ - loader: require("loader").Loader({ paths: paths, debug: require.debug - }), + loader: require("loader").Loader({ paths: paths, debug: require.debug }), modules: modules, debug: require.debug }); diff --git a/test/test.wsf b/test/test.wsf deleted file mode 100644 index ceb42c8..0000000 --- a/test/test.wsf +++ /dev/null @@ -1,4 +0,0 @@ - - - - From 227a40e02b9c2f85fd547b9d138e35ee2c3b99a5 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Sat, 8 May 2010 17:12:44 -0500 Subject: [PATCH 06/14] compliant! --- require.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/require.js b/require.js index fafb953..4bd3617 100644 --- a/require.js +++ b/require.js @@ -30,7 +30,7 @@ global = {}; (function (exports) { exports.engine = "jscript"; -exports.debug = "require.debug"; +exports.debug = require.debug; // We're treating paths on ASP like Unix paths, so misreport the os. // Hey, IE's been calling itself Mozilla all these years, so this is nothing. @@ -41,26 +41,22 @@ exports.Module = function (text, path, line) { // that takes the inject properties return function (inject) { inject = inject || {}; - var names = [], result; + var names = [], values = [], result; for (var name in inject) { if (Object.prototype.hasOwnProperty.call(inject, name)) { names.push(name); + values.push(inject[name]); } } // JScript's eval is not ES3 compliant, so the function needs to be // assigned to a variable. // @see http://www.bigresource.com/ASP-JScript-eval-bug-6nZST3Bk.html - eval("result = function (" + names.join(", ") + ") { " + text + " };"); - //eval("result = function (require, exports, module) { try {" + text + - // Fancy error augmentation surgery - //"} catch (e) {" + - //"throw new e.constructor(e.description + ' (in module: ' + path + ')');" + - //"}};" - //); - return result; - }; + eval("result = function (" + names.join(", ") + ") { " + text + "};"); + + return result.apply(null, values); + } }; })(modules.engine = {}); From 3f1a045494bb53eb579787a4a710d9e9e12149e0 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Sat, 8 May 2010 19:55:45 -0500 Subject: [PATCH 07/14] Get error with path --- require.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/require.js b/require.js index 4bd3617..4645ebe 100644 --- a/require.js +++ b/require.js @@ -41,7 +41,7 @@ exports.Module = function (text, path, line) { // that takes the inject properties return function (inject) { inject = inject || {}; - var names = [], values = [], result; + var names = [], values = [], result, ee; for (var name in inject) { if (Object.prototype.hasOwnProperty.call(inject, name)) { @@ -55,7 +55,15 @@ exports.Module = function (text, path, line) { // @see http://www.bigresource.com/ASP-JScript-eval-bug-6nZST3Bk.html eval("result = function (" + names.join(", ") + ") { " + text + "};"); - return result.apply(null, values); + try { + return result.apply(null, values); + } catch (e) { + // rethrow error with path + ee = new Error(e.number, e.description + " (in " + path + ")"); + ee.name = e.name; + ee.message = e.message; + throw ee; + } } }; From 84658cce1346e35d72cf7e68121f8f5400a5f157 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Sat, 8 May 2010 20:55:14 -0500 Subject: [PATCH 08/14] Add engine requires --- require.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/require.js b/require.js index 4645ebe..8af20e1 100644 --- a/require.js +++ b/require.js @@ -940,4 +940,12 @@ require = require("sandbox").Sandbox({ debug: require.debug }); +// Add engine-specific system/file if available +try { + require("system-engine"); + require("file-engine"); +} catch (e) { + if (e.description.indexOf("require error") === -1) { throw e; } +} + })(this); From a235e0b1a5db83895d039878241f801a19dd7ef2 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Sat, 8 May 2010 21:30:00 -0500 Subject: [PATCH 09/14] lint --- require.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/require.js b/require.js index 8af20e1..5d93a24 100644 --- a/require.js +++ b/require.js @@ -59,12 +59,12 @@ exports.Module = function (text, path, line) { return result.apply(null, values); } catch (e) { // rethrow error with path - ee = new Error(e.number, e.description + " (in " + path + ")"); + ee = Error(e.number, e.description + " (in " + path + ")"); ee.name = e.name; ee.message = e.message; throw ee; } - } + }; }; })(modules.engine = {}); @@ -404,7 +404,7 @@ var fso = new ActiveXObject("Scripting.FileSystemObject"), stream = new ActiveXObject("ADODB.Stream"); // Wrap some calls in Server.mapPath on ASP -function m(path) { return Server.mapPath(path) } +function m(path) { return Server.mapPath(path); } exports.isFile = function (path) { return fso.fileExists(m(path)); From 039bd1583eb61995dd3a82c2e567e89898a218ef Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Sat, 8 May 2010 22:15:39 -0500 Subject: [PATCH 10/14] updated docs --- README.markdown | 12 ++++++------ package.json | 21 +++++++++++++-------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/README.markdown b/README.markdown index 70bd8fa..583865b 100644 --- a/README.markdown +++ b/README.markdown @@ -1,16 +1,16 @@ CommonJScript ============= -CommonJScript is an implementation of the [CommonJS API](http://commonjs.org/) for Classic ASP and Windows Script Host. +CommonJScript is an implementation of the [CommonJS API](http://commonjs.org/) for Classic ASP. The [Modules/1.0](http://commonjs.org/specs/modules/1.0/) specification is currently implemented. Various others are planned for the future. -A few rudimentary modules are included in the *lib* directory. - Usage ----- -*require.js* can be included in a ASP page or WScript job to provide the `require` function. +*require.js* can be included in a ASP page or to provide the `require` function. + +Some other basic modules (system, engine, file) are also included in that file. Tests ----- @@ -20,12 +20,12 @@ The [CommonJS modules test suite](http://github.com/commonjs/commonjs/tree/maste git submodule init git submodule update -Run `cscript test.wsf` from the *test* directory for Windows Script Host (CLI), or load *test.asp* on ASP. +Load *test/index.asp* on ASP to run the tests. `debug=1` can be passed in the query string to turn `require.debug` on. Acknowledgements ---------------- -The module loading code is mostly taken from [Narwhal](http://narwhaljs.org). +Most of the code is taken directly from [Narwhal](http://narwhaljs.org) and [narwhal-lib](http://github.com/kriskowal/narwhal-lib/), so thanks to everyone working on those. License ------- diff --git a/package.json b/package.json index 56be46b..974599b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "commonjscript", - "description": "An implementation of the CommonJS API for Classic ASP and Windows Script Host", - "version": "0.0.0", + "description": "An implementation of the CommonJS API for Classic ASP", + "version": "0.1.0", "maintainers": [{ "name": "Nathan L Smith", "email": "nlloyds@gmail.com", @@ -12,16 +12,21 @@ "url": "http://www.opensource.org/licenses/mit-license.php" }], "bugs": "http://github.com/smith/commonjscript/issues", - "keywords": ["engine", "jscript", "windows", "asp", "wsh", "modules"], + "keywords": ["engine", "jscript", "windows", "asp", "modules"], "repositories": [{ "type": "git", "url": "git://github.com/smith/commonjscript.git" }], - "contributors": [{ - "name": "Nathan L Smith", - "email": "nlloyds@gmail.com", - "web": "http://nlsmith.com/" - }], + "contributors": [ + { + "name": "Nathan L Smith", + "email": "nlloyds@gmail.com", + "web": "http://nlsmith.com/" + }, + { "name": "Kris Kowal" }, + { "name": "Tom Robinson" }, + { "name": "Christoph Dorn" } + ], "os": ["windows"], "engine": ["jscript"], "implements": ["CommonJS/Modules/1.0"] From 9380fd71e0692e32073ee8bbae85784767efcf05 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Mon, 10 May 2010 20:23:19 -0500 Subject: [PATCH 11/14] Rename file > fs --- require.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/require.js b/require.js index 5d93a24..7c94755 100644 --- a/require.js +++ b/require.js @@ -95,7 +95,7 @@ exports.env = {}; print = require("system").print; // Add print function here -// File +// FS // // From narwhal-lib/lib/narwhal/fs-boot.js // ============================================================================= @@ -396,7 +396,7 @@ exports.extension = function (path) { }; })(modules["narwhal/fs"] = {}); -// File - platform specific +// FS - platform specific // ============================================================================= (function (exports) { @@ -423,7 +423,7 @@ exports.read = function (path, options) { return text; }; -})(modules.file = modules["narwhal/fs"]); +})(modules.fs = modules["narwhal/fs"]); // Loader // @@ -943,7 +943,7 @@ require = require("sandbox").Sandbox({ // Add engine-specific system/file if available try { require("system-engine"); - require("file-engine"); + require("fs-engine"); } catch (e) { if (e.description.indexOf("require error") === -1) { throw e; } } From ba9fca2fbed2a6bbd12c1fa12a0b806c876aeabd Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Mon, 10 May 2010 20:48:41 -0500 Subject: [PATCH 12/14] fix split method --- require.js | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/require.js b/require.js index 7c94755..0bc94b8 100644 --- a/require.js +++ b/require.js @@ -25,6 +25,107 @@ require = function (id) { return modules[id]; }; // global // use an empty object in its place. global = {}; +// JScript's split method is not ECMA compliant. Replace it. +// ============================================================================= + +/* Cross-Browser Split 1.0.1 +(c) Steven Levithan ; MIT License +An ECMA-compliant, uniform cross-browser split method */ + +var cbSplit; + +// avoid running twice, which would break `cbSplit._nativeSplit`'s reference to the native `split` +if (!cbSplit) { + +cbSplit = function (str, separator, limit) { + // if `separator` is not a regex, use the native `split` + if (Object.prototype.toString.call(separator) !== "[object RegExp]") { + return cbSplit._nativeSplit.call(str, separator, limit); + } + + var output = [], + lastLastIndex = 0, + flags = (separator.ignoreCase ? "i" : "") + + (separator.multiline ? "m" : "") + + (separator.sticky ? "y" : ""), + separator = RegExp(separator.source, flags + "g"), // make `global` and avoid `lastIndex` issues by working with a copy + separator2, match, lastIndex, lastLength; + + str = str + ""; // type conversion + if (!cbSplit._compliantExecNpcg) { + separator2 = RegExp("^" + separator.source + "$(?!\\s)", flags); // doesn't need /g or /y, but they don't hurt + } + + /* behavior for `limit`: if it's... + - `undefined`: no limit. + - `NaN` or zero: return an empty array. + - a positive number: use `Math.floor(limit)`. + - a negative number: no limit. + - other: type-convert, then use the above rules. */ + if (limit === undefined || +limit < 0) { + limit = Infinity; + } else { + limit = Math.floor(+limit); + if (!limit) { + return []; + } + } + + while (match = separator.exec(str)) { + lastIndex = match.index + match[0].length; // `separator.lastIndex` is not reliable cross-browser + + if (lastIndex > lastLastIndex) { + output.push(str.slice(lastLastIndex, match.index)); + + // fix browsers whose `exec` methods don't consistently return `undefined` for nonparticipating capturing groups + if (!cbSplit._compliantExecNpcg && match.length > 1) { + match[0].replace(separator2, function () { + for (var i = 1; i < arguments.length - 2; i++) { + if (arguments[i] === undefined) { + match[i] = undefined; + } + } + }); + } + + if (match.length > 1 && match.index < str.length) { + Array.prototype.push.apply(output, match.slice(1)); + } + + lastLength = match[0].length; + lastLastIndex = lastIndex; + + if (output.length >= limit) { + break; + } + } + + if (separator.lastIndex === match.index) { + separator.lastIndex++; // avoid an infinite loop + } + } + + if (lastLastIndex === str.length) { + if (lastLength || !separator.test("")) { + output.push(""); + } + } else { + output.push(str.slice(lastLastIndex)); + } + + return output.length > limit ? output.slice(0, limit) : output; +}; + +cbSplit._compliantExecNpcg = /()??/.exec("")[1] === undefined; // NPCG: nonparticipating capturing group +cbSplit._nativeSplit = String.prototype.split; + +} // end `if (!cbSplit)` + +// for convenience... +String.prototype.split = function (separator, limit) { + return cbSplit(this, separator, limit); +}; + // Engine // ============================================================================= (function (exports) { From 7b6d1c868d13ca7c339a4d135b54d9f6efb18781 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Wed, 19 May 2010 15:30:25 -0500 Subject: [PATCH 13/14] system.collectionToObject & system.env --- require.js | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/require.js b/require.js index 0bc94b8..c6ee7b9 100644 --- a/require.js +++ b/require.js @@ -189,8 +189,44 @@ exports.stdio = { print: exports.stdout.print }; exports.print = exports.stdout.print; -// TODO -exports.env = {}; +// Convert a JScript/VBScript collection to a plain ol object +// XXX: Not standard +exports.collectionToObject = function (c) { + var o = {}, // new object + e = new Enumerator(c), + cnt = 0, // count + item; // item + + // Iterate through collection + while (!e.atEnd()) { + item = e.item(); + // null items have no count + try { cnt = c(item).count; } + catch (er) { cnt = -1; } + + if (cnt > 1) { // multiple items + o[item] = []; + for (var i = 1; i < cnt + 1; i += 1) { + o[item].push(String(c(item)(i))); + } + } else if (cnt === 0) { o[item] = undefined; } + else { o[item] = c(item); } + + e.moveNext(); + } + + for (i in o) { + // Stringify everything but null, undefined, and array + if (Object.prototype.hasOwnProperty.call(o, i) && o[i] !== null && + typeof o[i] !== "undefined" && typeof o[i].length === "undefined") { + o[i] = String(o[i]); + } + } + + return o; +}; + +exports.env = exports.collectionToObject(Request.ServerVariables); })(modules.system = {}); From aa1e78827abf3283847ac80bab66e04e264edb39 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Fri, 21 May 2010 15:19:03 -0500 Subject: [PATCH 14/14] update to track latest narwhal/lib --- require.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/require.js b/require.js index c6ee7b9..80a1820 100644 --- a/require.js +++ b/require.js @@ -169,6 +169,7 @@ exports.Module = function (text, path, line) { }; })(modules.engine = {}); +modules["narwhal/engine"] = modules.engine; // System // ============================================================================= @@ -575,7 +576,7 @@ exports.read = function (path, options) { // occur here have to be manually accounted for (who loads // the loader?) -var ENGINE = require("engine"); +var ENGINE = require("narwhal/engine"); // HACK: the stars prevent the file module from being sent to browser // clients with the regexen we're using. we need a real solution // for this.