From ebd64887525ea3cc9a5bc879196d74accbe6208b Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Thu, 5 Apr 2012 14:59:01 +0200 Subject: [PATCH] better free var filling, node.js test --- .gitignore | 3 +- README.md | 15 +- bin/webpack.js | 85 +- buildin/__webpack_console.js | 21 +- buildin/__webpack_dirname.js | 1 + buildin/__webpack_filename.js | 1 + buildin/__webpack_module.js | 10 +- buildin/__webpack_process.js | 43 +- buildin/web_modules/assert.js | 2 +- buildin/web_modules/buffer.js | 1130 +++++++++++++++ buildin/web_modules/buffer_ieee754.js | 84 ++ buildin/web_modules/events.js | 13 +- buildin/web_modules/path.js | 450 ++++++ buildin/web_modules/punycode.js | 512 +++++++ buildin/web_modules/querystring.js | 142 ++ buildin/web_modules/url.js | 635 +++++++++ examples/build-common.js | 29 + examples/buildAll.js | 11 + .../code-splitted-require.context/README.md | 118 +- .../code-splitted-require.context/build.js | 19 +- .../code-splitted-require.context/template.md | 25 + examples/code-splitting/README.md | 63 +- examples/code-splitting/build.js | 19 +- examples/code-splitting/template.md | 38 + examples/coffee-script/README.md | 64 +- examples/coffee-script/build.js | 19 +- examples/coffee-script/template.md | 38 + examples/loader/README.md | 96 +- examples/loader/build.js | 19 +- examples/loader/template.md | 47 + examples/require.context/README.md | 97 +- examples/require.context/build.js | 19 +- examples/require.context/template.md | 43 + examples/template-common.js | 16 + lib/parse.js | 39 +- lib/templateAsync.js | 1 + lib/webpack.js | 5 +- lib/writeSource.js | 62 +- package.json | 2 +- test/browsertest/build.js | 56 +- test/browsertest/lib/index.web.js | 5 + test/browsertest/nodetests/common.js | 146 ++ .../nodetests/fixtures/global/plain.js | 25 + test/browsertest/nodetests/index.js | 26 + .../nodetests/simple/test-assert.js | 285 ++++ ...test-event-emitter-check-listener-leaks.js | 58 + .../test-event-emitter-modify-in-emit.js | 77 ++ .../simple/test-event-emitter-num-args.js | 48 + ...test-event-emitter-remove-all-listeners.js | 53 + .../nodetests/simple/test-global.js | 39 + .../simple/test-next-tick-doesnt-hang.js | 30 + .../simple/test-next-tick-ordering2.js | 38 + .../browsertest/nodetests/simple/test-path.js | 275 ++++ .../nodetests/simple/test-querystring.js | 230 +++ test/browsertest/nodetests/simple/test-sys.js | 129 ++ .../simple/test-timers-zero-timeout.js | 59 + .../nodetests/simple/test-timers.js | 71 + test/browsertest/nodetests/simple/test-url.js | 1229 +++++++++++++++++ .../nodetests/simple/test-util-format.js | 62 + .../nodetests/simple/test-util-inspect.js | 102 ++ .../browsertest/nodetests/simple/test-util.js | 71 + test/browsertest/test.html | 8 +- 62 files changed, 6738 insertions(+), 420 deletions(-) create mode 100644 buildin/__webpack_dirname.js create mode 100644 buildin/__webpack_filename.js create mode 100644 buildin/web_modules/buffer.js create mode 100644 buildin/web_modules/buffer_ieee754.js create mode 100644 buildin/web_modules/path.js create mode 100644 buildin/web_modules/punycode.js create mode 100644 buildin/web_modules/querystring.js create mode 100644 buildin/web_modules/url.js create mode 100644 examples/build-common.js create mode 100644 examples/buildAll.js create mode 100644 examples/code-splitted-require.context/template.md create mode 100644 examples/code-splitting/template.md create mode 100644 examples/coffee-script/template.md create mode 100644 examples/loader/template.md create mode 100644 examples/require.context/template.md create mode 100644 examples/template-common.js create mode 100644 test/browsertest/nodetests/common.js create mode 100644 test/browsertest/nodetests/fixtures/global/plain.js create mode 100644 test/browsertest/nodetests/index.js create mode 100644 test/browsertest/nodetests/simple/test-assert.js create mode 100644 test/browsertest/nodetests/simple/test-event-emitter-check-listener-leaks.js create mode 100644 test/browsertest/nodetests/simple/test-event-emitter-modify-in-emit.js create mode 100644 test/browsertest/nodetests/simple/test-event-emitter-num-args.js create mode 100644 test/browsertest/nodetests/simple/test-event-emitter-remove-all-listeners.js create mode 100644 test/browsertest/nodetests/simple/test-global.js create mode 100644 test/browsertest/nodetests/simple/test-next-tick-doesnt-hang.js create mode 100644 test/browsertest/nodetests/simple/test-next-tick-ordering2.js create mode 100644 test/browsertest/nodetests/simple/test-path.js create mode 100644 test/browsertest/nodetests/simple/test-querystring.js create mode 100644 test/browsertest/nodetests/simple/test-sys.js create mode 100644 test/browsertest/nodetests/simple/test-timers-zero-timeout.js create mode 100644 test/browsertest/nodetests/simple/test-timers.js create mode 100644 test/browsertest/nodetests/simple/test-url.js create mode 100644 test/browsertest/nodetests/simple/test-util-format.js create mode 100644 test/browsertest/nodetests/simple/test-util-inspect.js create mode 100644 test/browsertest/nodetests/simple/test-util.js diff --git a/.gitignore b/.gitignore index 679a47b88..777981881 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /node_modules /test/js /test/browsertest/js -/examples/*/js \ No newline at end of file +/test/browsertest/node_modules/vm-browserify +/examples/*/js diff --git a/README.md b/README.md index 54056b9eb..830fd1f72 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,9 @@ Initially only `web.js` is included (and loaded) by your application. `1.web.js` is loaded when the call to `require.ensure` happens. After the second bundle (`1.web.js`) is loaded, the callback function will be invoked. -See [details](modules-webpack/tree/master/examples/code-splitting) for exact output. +See [details](/sokra/modules-webpack/tree/master/examples/code-splitting) for exact output. + +See [more examples](/sokra/modules-webpack/tree/master/examples). ## Reusing node.js code @@ -250,9 +252,16 @@ Some credit goes to the browserify contributors, you can use replacements provid Included simple replacements: -* `assert`: copy of node.js' version +* `assert`: copy of node.js' version, small change +* `buffer`: copy of node-browserify's version +* `buffer_ieee754`: copy of node-browserify's version * `child_process`: disabled * `events`: copy of node.js' version +* `path`: copy of node.js' version +* `punycode`: copy of node.js' version, one line removed +* `querystring`: copy of node.js' version +* `string_decoder`: copy of node.js' version +* `url`: copy of node.js' version * `util`: copy of node.js' version Here is a list of possible useful replacements: (intentionally not by default) @@ -369,7 +378,7 @@ free module variables which are replaced with a module. ex. `{ "$": "jquery" }` `function(err, source / stats)` `source` if `options.output` is not set -else `stats` as json see [example](modules-webpack/tree/master/examples/code-splitting) +else `stats` as json see [example](/sokra/modules-webpack/tree/master/examples/code-splitting) ## Comparison diff --git a/bin/webpack.js b/bin/webpack.js index 1543cc852..2f3a0de7c 100644 --- a/bin/webpack.js +++ b/bin/webpack.js @@ -40,6 +40,10 @@ var argv = require("optimist") .describe("json", "Output Stats as JSON") .default("json", false) + .boolean("verbose") + .describe("verbose", "Output dependencies in Stats") + .default("verbose", false) + .string("alias") .describe("alias", "Set a alias name for a module. ex. http=http-browserify") @@ -128,50 +132,75 @@ if(argv.single) { if(argv.json) console.log(util.inspect(stats, false, 10, argv.colors)); else { - console.log("Chunks: \033[1m" + stats.chunkCount + "\033[22m"); - console.log("Modules: \033[1m" + stats.modulesCount + "\033[22m"); - console.log("Modules including duplicates: \033[1m" + stats.modulesIncludingDuplicates + "\033[22m"); - console.log("Modules pre chunk: \033[1m" + stats.modulesPerChunk + "\033[22m"); - console.log("Modules first chunk: \033[1m" + stats.modulesFirstChunk + "\033[22m"); + function c(str) { + return argv.colors ? str : ""; + } + console.log("Chunks: "+c("\033[1m") + stats.chunkCount + c("\033[22m")); + console.log("Modules: "+c("\033[1m") + stats.modulesCount + c("\033[22m")); + console.log("Modules including duplicates: "+c("\033[1m") + stats.modulesIncludingDuplicates + c("\033[22m")); + console.log("Modules pre chunk: "+c("\033[1m") + stats.modulesPerChunk + c("\033[22m")); + console.log("Modules first chunk: "+c("\033[1m") + stats.modulesFirstChunk + c("\033[22m")); if(stats.fileSizes) for(var file in stats.fileSizes) { - console.log("\033[1m" + sprintf("%" + (5 + options.output.length) + "s", file) + "\033[22m: \033[1m" + sprintf("%8d", stats.fileSizes[file]) + "\033[22m characters"); + console.log(c("\033[1m") + sprintf("%" + (5 + options.output.length) + "s", file) + c("\033[22m")+": "+c("\033[1m") + sprintf("%8d", stats.fileSizes[file]) + c("\033[22m") + " characters"); }; + var cwd = process.cwd(); + var cwdParent = path.dirname(cwd); + var buildins = path.join(__dirname, ".."); + cwd = cwd.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + cwd = new RegExp("^" + cwd + "|(!)" + cwd, "g"); + cwdParent = cwdParent.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + cwdParent = new RegExp("^" + cwdParent + "|(!)" + cwdParent, "g"); + buildins = buildins.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + buildins = new RegExp("^" + buildins + "|(!)" + buildins, "g"); + function compressFilename(filename) { + if(!filename) + return filename; + filename = filename.replace(cwd, "!."); + filename = filename.replace(cwdParent, "!.."); + filename = filename.replace(buildins, "!(webpack)"); + return filename.replace(/^!/, ""); + } if(stats.fileModules) { for(var file in stats.fileModules) { - console.log("\033[1m\033[32m" + file + "\033[39m\033[22m"); + console.log(c("\033[1m\033[32m") + file + c("\033[39m\033[22m")); var modules = stats.fileModules[file]; modules.forEach(function(module) { - console.log(" \033[1m" + sprintf("%3s", module.id+"") + " " + (module.filename || (module.dirname && ("generated " + module.dirname)) || "generated") + "\033[22m"); - module.reasons.forEach(function(reason) { - switch(reason.type) { - case "require": - console.log(" \033[36mrequire (" + reason.count + "x) from " + reason.filename + "\033[39m"); - break; - case "context": - console.log(" \033[90mcontext from " + reason.filename + "\033[39m"); - break; - case "async require": - console.log(" \033[35masync require (" + reason.count + "x) from " + reason.filename + "\033[39m"); - break; - case "async context": - console.log(" \033[35masync context from " + reason.filename + "\033[39m"); - break; - default: - console.log(" \033[31m" + reason.type + "\033[39m"); - } - }); + console.log(" "+c("\033[1m") + sprintf("%3s", module.id+"") + " " + + (compressFilename(module.filename) || + (module.dirname && ("[context] " + compressFilename(module.dirname))) || + "[unknown]") + c("\033[22m")); + if(argv.verbose) { + module.reasons.forEach(function(reason) { + switch(reason.type) { + case "require": + console.log(" "+c("\033[36m")+"require (" + reason.count + "x) from " + compressFilename(reason.filename) + c("\033[39m")); + break; + case "context": + console.log(" "+c("\033[90m")+"context from " + compressFilename(reason.filename) + c("\033[39m")); + break; + case "async require": + console.log(" "+c("\033[35m")+"async require (" + reason.count + "x) from " + compressFilename(reason.filename) + c("\033[39m")); + break; + case "async context": + console.log(" "+c("\033[35ma")+"sync context from " + compressFilename(reason.filename) + c("\033[39m")); + break; + default: + console.log(" "+c("\033[31m") + reason.type + c("\033[39m")); + } + }); + } }); } } if(stats.warnings) { stats.warnings.forEach(function(warning) { - console.log("\033[1m\033[33mWARNING: " + warning + "\033[39m\033[22m"); + console.log(c("\033[1m\033[33m")+"WARNING: " + warning + c("\033[39m\033[22m")); }); } if(stats.errors) { stats.errors.forEach(function(error) { - console.log("\033[1m\033[31mERROR: " + error + "\033[39m\033[22m"); + console.log(c("\033[1m\033[31m")+"ERROR: " + error + c("\033[39m\033[22m")); }); } } diff --git a/buildin/__webpack_console.js b/buildin/__webpack_console.js index e484d9bce..3dc3ae682 100644 --- a/buildin/__webpack_console.js +++ b/buildin/__webpack_console.js @@ -1,15 +1,14 @@ var console = window.console; -exports.log = (console && console.log) || function() {}; -exports.info = (console && console.info) || function() {}; -exports.error = (console && console.error) || function() {}; -exports.warn = (console && console.warn) || function() {}; -exports.dir = (console && console.dir) || function() {}; -exports.time = (console && console.time) || function(label) { +module.exports = console; +for(var name in {log:1, info:1, error:1, warn:1, dir:1, trace:1, assert:1}) + if(!console[name]) + console[name] = function() {}; +if(!console.time) +console.time = function(label) { times[label] = Date.now(); }; -exports.timeEnd = (console && console.timeEnd) || function() { +if(!console.timeEnd) +console.timeEnd = function() { var duration = Date.now() - times[label]; - exports.log('%s: %dms', label, duration); -}; -exports.trace = (console && console.trace) || function() {}; -exports.assert = (console && console.assert) || function() {}; \ No newline at end of file + console.log('%s: %dms', label, duration); +}; \ No newline at end of file diff --git a/buildin/__webpack_dirname.js b/buildin/__webpack_dirname.js new file mode 100644 index 000000000..7c0959873 --- /dev/null +++ b/buildin/__webpack_dirname.js @@ -0,0 +1 @@ +module.exports = "."; \ No newline at end of file diff --git a/buildin/__webpack_filename.js b/buildin/__webpack_filename.js new file mode 100644 index 000000000..52875b6dc --- /dev/null +++ b/buildin/__webpack_filename.js @@ -0,0 +1 @@ +module.exports = "./index.js"; \ No newline at end of file diff --git a/buildin/__webpack_module.js b/buildin/__webpack_module.js index 55edcc846..669f15641 100644 --- a/buildin/__webpack_module.js +++ b/buildin/__webpack_module.js @@ -1,2 +1,8 @@ -exports.deprecate = function() {}; -exports.id = "webpack"; \ No newline at end of file +module.exports = function(module) { + if(!module.webpackPolyfill) { + module.deprecate = function() {}; + module.id = "webpack"; + module.webpackPolyfill = 1; + } + return module; +} diff --git a/buildin/__webpack_process.js b/buildin/__webpack_process.js index 842da7f3c..0251c34dd 100644 --- a/buildin/__webpack_process.js +++ b/buildin/__webpack_process.js @@ -6,16 +6,47 @@ if(Object.prototype.__defineGetter__) { exports.title = window.title; } exports.version = exports.arch = -exports.platform = exports.execPath = "webpack"; +exports.execPath = "webpack"; +exports.platform = "browser"; // TODO stdin, stdout, stderr exports.argv = ["webpack", "browser"]; exports.pid = 1; -exports.nextTick = function(func) { - setTimeout(func, 0); +exports.nextTick = (function(func) { + // from https://github.com/substack/node-browserify/blob/master/wrappers/process.js + var queue = []; + var canPost = typeof window !== 'undefined' + && window.postMessage && window.addEventListener + ; + + if (canPost) { + window.addEventListener('message', function (ev) { + if (ev.source === window && ev.data === 'webpack-tick') { + ev.stopPropagation(); + if (queue.length > 0) { + var fn = queue.shift(); + fn(); + } + } + }, true); + } + + return function (fn) { + if (canPost) { + queue.push(fn); + window.postMessage('webpack-tick', '*'); + } + else setTimeout(fn, 0); + }; +}()); +exports.cwd = function() { + return "/app"; } exports.exit = exports.kill = -exports.chdir = exports.cwd = +exports.chdir = exports.umask = exports.dlopen = exports.uptime = exports.memoryUsage = -exports.uvCounters = exports.binding = function() {}; -exports.features = {}; \ No newline at end of file +exports.uvCounters = function() {}; +exports.features = {}; +exports.binding = function(str) { + return {}; +} \ No newline at end of file diff --git a/buildin/web_modules/assert.js b/buildin/web_modules/assert.js index 7e932be14..045a5c783 100644 --- a/buildin/web_modules/assert.js +++ b/buildin/web_modules/assert.js @@ -156,7 +156,7 @@ function _deepEqual(actual, expected) { if (actual === expected) { return true; - } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { + } else if (require("buffer").Buffer.isBuffer(actual) && require("buffer").Buffer.isBuffer(expected)) { if (actual.length != expected.length) return false; for (var i = 0; i < actual.length; i++) { diff --git a/buildin/web_modules/buffer.js b/buildin/web_modules/buffer.js new file mode 100644 index 000000000..5c14c5b12 --- /dev/null +++ b/buildin/web_modules/buffer.js @@ -0,0 +1,1130 @@ +function SlowBuffer (size) { + this.length = size; +}; + +var assert = require('assert'); + +exports.INSPECT_MAX_BYTES = 50; + + +function toHex(n) { + if (n < 16) return '0' + n.toString(16); + return n.toString(16); +} + + +SlowBuffer.prototype.inspect = function() { + var out = [], + len = this.length; + for (var i = 0; i < len; i++) { + out[i] = toHex(this[i]); + if (i == exports.INSPECT_MAX_BYTES) { + out[i + 1] = '...'; + break; + } + } + return ''; +}; + + +SlowBuffer.prototype.hexSlice = function(start, end) { + var len = this.length; + + if (!start || start < 0) start = 0; + if (!end || end < 0 || end > len) end = len; + + var out = ''; + for (var i = start; i < end; i++) { + out += toHex(this[i]); + } + return out; +}; + + +SlowBuffer.prototype.toString = function(encoding, start, end) { + encoding = String(encoding || 'utf8').toLowerCase(); + start = +start || 0; + if (typeof end == 'undefined') end = this.length; + + // Fastpath empty strings + if (+end == start) { + return ''; + } + + switch (encoding) { + case 'hex': + return this.hexSlice(start, end); + + case 'utf8': + case 'utf-8': + return this.utf8Slice(start, end); + + case 'ascii': + return this.asciiSlice(start, end); + + case 'binary': + return this.binarySlice(start, end); + + case 'base64': + return this.base64Slice(start, end); + + case 'ucs2': + case 'ucs-2': + return this.ucs2Slice(start, end); + + default: + throw new Error('Unknown encoding'); + } +}; + + +SlowBuffer.prototype.hexWrite = function(string, offset, length) { + offset = +offset || 0; + var remaining = this.length - offset; + if (!length) { + length = remaining; + } else { + length = +length; + if (length > remaining) { + length = remaining; + } + } + + // must be an even number of digits + var strLen = string.length; + if (strLen % 2) { + throw new Error('Invalid hex string'); + } + if (length > strLen / 2) { + length = strLen / 2; + } + for (var i = 0; i < length; i++) { + var byte = parseInt(string.substr(i * 2, 2), 16); + if (isNaN(byte)) throw new Error('Invalid hex string'); + this[offset + i] = byte; + } + SlowBuffer._charsWritten = i * 2; + return i; +}; + + +SlowBuffer.prototype.write = function(string, offset, length, encoding) { + // Support both (string, offset, length, encoding) + // and the legacy (string, encoding, offset, length) + if (isFinite(offset)) { + if (!isFinite(length)) { + encoding = length; + length = undefined; + } + } else { // legacy + var swap = encoding; + encoding = offset; + offset = length; + length = swap; + } + + offset = +offset || 0; + var remaining = this.length - offset; + if (!length) { + length = remaining; + } else { + length = +length; + if (length > remaining) { + length = remaining; + } + } + encoding = String(encoding || 'utf8').toLowerCase(); + + switch (encoding) { + case 'hex': + return this.hexWrite(string, offset, length); + + case 'utf8': + case 'utf-8': + return this.utf8Write(string, offset, length); + + case 'ascii': + return this.asciiWrite(string, offset, length); + + case 'binary': + return this.binaryWrite(string, offset, length); + + case 'base64': + return this.base64Write(string, offset, length); + + case 'ucs2': + case 'ucs-2': + return this.ucs2Write(string, offset, length); + + default: + throw new Error('Unknown encoding'); + } +}; + + +// slice(start, end) +SlowBuffer.prototype.slice = function(start, end) { + if (end === undefined) end = this.length; + + if (end > this.length) { + throw new Error('oob'); + } + if (start > end) { + throw new Error('oob'); + } + + return new Buffer(this, end - start, +start); +}; + + +function coerce(length) { + // Coerce length to a number (possibly NaN), round up + // in case it's fractional (e.g. 123.456) then do a + // double negate to coerce a NaN to 0. Easy, right? + length = ~~Math.ceil(+length); + return length < 0 ? 0 : length; +} + + +// Buffer + +function Buffer(subject, encoding, offset) { + if (!(this instanceof Buffer)) { + return new Buffer(subject, encoding, offset); + } + + var type; + + // Are we slicing? + if (typeof offset === 'number') { + this.length = coerce(encoding); + this.parent = subject; + this.offset = offset; + } else { + // Find the length + switch (type = typeof subject) { + case 'number': + this.length = coerce(subject); + break; + + case 'string': + this.length = Buffer.byteLength(subject, encoding); + break; + + case 'object': // Assume object is an array + this.length = coerce(subject.length); + break; + + default: + throw new Error('First argument needs to be a number, ' + + 'array or string.'); + } + + if (this.length > Buffer.poolSize) { + // Big buffer, just alloc one. + this.parent = new SlowBuffer(this.length); + this.offset = 0; + + } else { + // Small buffer. + if (!pool || pool.length - pool.used < this.length) allocPool(); + this.parent = pool; + this.offset = pool.used; + pool.used += this.length; + } + + // Treat array-ish objects as a byte array. + if (isArrayIsh(subject)) { + for (var i = 0; i < this.length; i++) { + this.parent[i + this.offset] = subject[i]; + } + } else if (type == 'string') { + // We are a string + this.length = this.write(subject, 0, encoding); + } + } + +} + +function isArrayIsh(subject) { + return Array.isArray(subject) || Buffer.isBuffer(subject) || + subject && typeof subject === 'object' && + typeof subject.length === 'number'; +} + +exports.SlowBuffer = SlowBuffer; +exports.Buffer = Buffer; + +Buffer.poolSize = 8 * 1024; +var pool; + +function allocPool() { + pool = new SlowBuffer(Buffer.poolSize); + pool.used = 0; +} + + +// Static methods +Buffer.isBuffer = function isBuffer(b) { + return b instanceof Buffer || b instanceof SlowBuffer; +}; + + +// Inspect +Buffer.prototype.inspect = function inspect() { + var out = [], + len = this.length; + + for (var i = 0; i < len; i++) { + out[i] = toHex(this.parent[i + this.offset]); + if (i == exports.INSPECT_MAX_BYTES) { + out[i + 1] = '...'; + break; + } + } + + return ''; +}; + + +Buffer.prototype.get = function get(i) { + if (i < 0 || i >= this.length) throw new Error('oob'); + return this.parent[this.offset + i]; +}; + + +Buffer.prototype.set = function set(i, v) { + if (i < 0 || i >= this.length) throw new Error('oob'); + return this.parent[this.offset + i] = v; +}; + + +// write(string, offset = 0, length = buffer.length-offset, encoding = 'utf8') +Buffer.prototype.write = function(string, offset, length, encoding) { + // Support both (string, offset, length, encoding) + // and the legacy (string, encoding, offset, length) + if (isFinite(offset)) { + if (!isFinite(length)) { + encoding = length; + length = undefined; + } + } else { // legacy + var swap = encoding; + encoding = offset; + offset = length; + length = swap; + } + + offset = +offset || 0; + var remaining = this.length - offset; + if (!length) { + length = remaining; + } else { + length = +length; + if (length > remaining) { + length = remaining; + } + } + encoding = String(encoding || 'utf8').toLowerCase(); + + var ret; + switch (encoding) { + case 'hex': + ret = this.parent.hexWrite(string, this.offset + offset, length); + break; + + case 'utf8': + case 'utf-8': + ret = this.parent.utf8Write(string, this.offset + offset, length); + break; + + case 'ascii': + ret = this.parent.asciiWrite(string, this.offset + offset, length); + break; + + case 'binary': + ret = this.parent.binaryWrite(string, this.offset + offset, length); + break; + + case 'base64': + // Warning: maxLength not taken into account in base64Write + ret = this.parent.base64Write(string, this.offset + offset, length); + break; + + case 'ucs2': + case 'ucs-2': + ret = this.parent.ucs2Write(string, this.offset + offset, length); + break; + + default: + throw new Error('Unknown encoding'); + } + + Buffer._charsWritten = SlowBuffer._charsWritten; + + return ret; +}; + + +// toString(encoding, start=0, end=buffer.length) +Buffer.prototype.toString = function(encoding, start, end) { + encoding = String(encoding || 'utf8').toLowerCase(); + + if (typeof start == 'undefined' || start < 0) { + start = 0; + } else if (start > this.length) { + start = this.length; + } + + if (typeof end == 'undefined' || end > this.length) { + end = this.length; + } else if (end < 0) { + end = 0; + } + + start = start + this.offset; + end = end + this.offset; + + switch (encoding) { + case 'hex': + return this.parent.hexSlice(start, end); + + case 'utf8': + case 'utf-8': + return this.parent.utf8Slice(start, end); + + case 'ascii': + return this.parent.asciiSlice(start, end); + + case 'binary': + return this.parent.binarySlice(start, end); + + case 'base64': + return this.parent.base64Slice(start, end); + + case 'ucs2': + case 'ucs-2': + return this.parent.ucs2Slice(start, end); + + default: + throw new Error('Unknown encoding'); + } +}; + + +// byteLength +Buffer.byteLength = SlowBuffer.byteLength; + + +// fill(value, start=0, end=buffer.length) +Buffer.prototype.fill = function fill(value, start, end) { + value || (value = 0); + start || (start = 0); + end || (end = this.length); + + if (typeof value === 'string') { + value = value.charCodeAt(0); + } + if (!(typeof value === 'number') || isNaN(value)) { + throw new Error('value is not a number'); + } + + if (end < start) throw new Error('end < start'); + + // Fill 0 bytes; we're done + if (end === start) return 0; + if (this.length == 0) return 0; + + if (start < 0 || start >= this.length) { + throw new Error('start out of bounds'); + } + + if (end < 0 || end > this.length) { + throw new Error('end out of bounds'); + } + + return this.parent.fill(value, + start + this.offset, + end + this.offset); +}; + + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function(target, target_start, start, end) { + var source = this; + start || (start = 0); + end || (end = this.length); + target_start || (target_start = 0); + + if (end < start) throw new Error('sourceEnd < sourceStart'); + + // Copy 0 bytes; we're done + if (end === start) return 0; + if (target.length == 0 || source.length == 0) return 0; + + if (target_start < 0 || target_start >= target.length) { + throw new Error('targetStart out of bounds'); + } + + if (start < 0 || start >= source.length) { + throw new Error('sourceStart out of bounds'); + } + + if (end < 0 || end > source.length) { + throw new Error('sourceEnd out of bounds'); + } + + // Are we oob? + if (end > this.length) { + end = this.length; + } + + if (target.length - target_start < end - start) { + end = target.length - target_start + start; + } + + return this.parent.copy(target.parent, + target_start + target.offset, + start + this.offset, + end + this.offset); +}; + + +// slice(start, end) +Buffer.prototype.slice = function(start, end) { + if (end === undefined) end = this.length; + if (end > this.length) throw new Error('oob'); + if (start > end) throw new Error('oob'); + + return new Buffer(this.parent, end - start, +start + this.offset); +}; + + +// Legacy methods for backwards compatibility. + +Buffer.prototype.utf8Slice = function(start, end) { + return this.toString('utf8', start, end); +}; + +Buffer.prototype.binarySlice = function(start, end) { + return this.toString('binary', start, end); +}; + +Buffer.prototype.asciiSlice = function(start, end) { + return this.toString('ascii', start, end); +}; + +Buffer.prototype.utf8Write = function(string, offset) { + return this.write(string, offset, 'utf8'); +}; + +Buffer.prototype.binaryWrite = function(string, offset) { + return this.write(string, offset, 'binary'); +}; + +Buffer.prototype.asciiWrite = function(string, offset) { + return this.write(string, offset, 'ascii'); +}; + +Buffer.prototype.readUInt8 = function(offset, noAssert) { + var buffer = this; + + if (!noAssert) { + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'Trying to read beyond buffer length'); + } + + return buffer[offset]; +}; + +function readUInt16(buffer, offset, isBigEndian, noAssert) { + var val = 0; + + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (isBigEndian) { + val = buffer[offset] << 8; + val |= buffer[offset + 1]; + } else { + val = buffer[offset]; + val |= buffer[offset + 1] << 8; + } + + return val; +} + +Buffer.prototype.readUInt16LE = function(offset, noAssert) { + return readUInt16(this, offset, false, noAssert); +}; + +Buffer.prototype.readUInt16BE = function(offset, noAssert) { + return readUInt16(this, offset, true, noAssert); +}; + +function readUInt32(buffer, offset, isBigEndian, noAssert) { + var val = 0; + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (isBigEndian) { + val = buffer[offset + 1] << 16; + val |= buffer[offset + 2] << 8; + val |= buffer[offset + 3]; + val = val + (buffer[offset] << 24 >>> 0); + } else { + val = buffer[offset + 2] << 16; + val |= buffer[offset + 1] << 8; + val |= buffer[offset]; + val = val + (buffer[offset + 3] << 24 >>> 0); + } + + return val; +} + +Buffer.prototype.readUInt32LE = function(offset, noAssert) { + return readUInt32(this, offset, false, noAssert); +}; + +Buffer.prototype.readUInt32BE = function(offset, noAssert) { + return readUInt32(this, offset, true, noAssert); +}; + + +/* + * Signed integer types, yay team! A reminder on how two's complement actually + * works. The first bit is the signed bit, i.e. tells us whether or not the + * number should be positive or negative. If the two's complement value is + * positive, then we're done, as it's equivalent to the unsigned representation. + * + * Now if the number is positive, you're pretty much done, you can just leverage + * the unsigned translations and return those. Unfortunately, negative numbers + * aren't quite that straightforward. + * + * At first glance, one might be inclined to use the traditional formula to + * translate binary numbers between the positive and negative values in two's + * complement. (Though it doesn't quite work for the most negative value) + * Mainly: + * - invert all the bits + * - add one to the result + * + * Of course, this doesn't quite work in Javascript. Take for example the value + * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of + * course, Javascript will do the following: + * + * > ~0xff80 + * -65409 + * + * Whoh there, Javascript, that's not quite right. But wait, according to + * Javascript that's perfectly correct. When Javascript ends up seeing the + * constant 0xff80, it has no notion that it is actually a signed number. It + * assumes that we've input the unsigned value 0xff80. Thus, when it does the + * binary negation, it casts it into a signed value, (positive 0xff80). Then + * when you perform binary negation on that, it turns it into a negative number. + * + * Instead, we're going to have to use the following general formula, that works + * in a rather Javascript friendly way. I'm glad we don't support this kind of + * weird numbering scheme in the kernel. + * + * (BIT-MAX - (unsigned)val + 1) * -1 + * + * The astute observer, may think that this doesn't make sense for 8-bit numbers + * (really it isn't necessary for them). However, when you get 16-bit numbers, + * you do. Let's go back to our prior example and see how this will look: + * + * (0xffff - 0xff80 + 1) * -1 + * (0x007f + 1) * -1 + * (0x0080) * -1 + */ +Buffer.prototype.readInt8 = function(offset, noAssert) { + var buffer = this; + var neg; + + if (!noAssert) { + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'Trying to read beyond buffer length'); + } + + neg = buffer[offset] & 0x80; + if (!neg) { + return (buffer[offset]); + } + + return ((0xff - buffer[offset] + 1) * -1); +}; + +function readInt16(buffer, offset, isBigEndian, noAssert) { + var neg, val; + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'Trying to read beyond buffer length'); + } + + val = readUInt16(buffer, offset, isBigEndian, noAssert); + neg = val & 0x8000; + if (!neg) { + return val; + } + + return (0xffff - val + 1) * -1; +} + +Buffer.prototype.readInt16LE = function(offset, noAssert) { + return readInt16(this, offset, false, noAssert); +}; + +Buffer.prototype.readInt16BE = function(offset, noAssert) { + return readInt16(this, offset, true, noAssert); +}; + +function readInt32(buffer, offset, isBigEndian, noAssert) { + var neg, val; + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + val = readUInt32(buffer, offset, isBigEndian, noAssert); + neg = val & 0x80000000; + if (!neg) { + return (val); + } + + return (0xffffffff - val + 1) * -1; +} + +Buffer.prototype.readInt32LE = function(offset, noAssert) { + return readInt32(this, offset, false, noAssert); +}; + +Buffer.prototype.readInt32BE = function(offset, noAssert) { + return readInt32(this, offset, true, noAssert); +}; + +function readFloat(buffer, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + return require('buffer_ieee754').readIEEE754(buffer, offset, isBigEndian, + 23, 4); +} + +Buffer.prototype.readFloatLE = function(offset, noAssert) { + return readFloat(this, offset, false, noAssert); +}; + +Buffer.prototype.readFloatBE = function(offset, noAssert) { + return readFloat(this, offset, true, noAssert); +}; + +function readDouble(buffer, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset + 7 < buffer.length, + 'Trying to read beyond buffer length'); + } + + return require('buffer_ieee754').readIEEE754(buffer, offset, isBigEndian, + 52, 8); +} + +Buffer.prototype.readDoubleLE = function(offset, noAssert) { + return readDouble(this, offset, false, noAssert); +}; + +Buffer.prototype.readDoubleBE = function(offset, noAssert) { + return readDouble(this, offset, true, noAssert); +}; + + +/* + * We have to make sure that the value is a valid integer. This means that it is + * non-negative. It has no fractional component and that it does not exceed the + * maximum allowed value. + * + * value The number to check for validity + * + * max The maximum value + */ +function verifuint(value, max) { + assert.ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + assert.ok(value >= 0, + 'specified a negative value for writing an unsigned value'); + + assert.ok(value <= max, 'value is larger than maximum value for type'); + + assert.ok(Math.floor(value) === value, 'value has a fractional component'); +} + +Buffer.prototype.writeUInt8 = function(value, offset, noAssert) { + var buffer = this; + + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xff); + } + + buffer[offset] = value; +}; + +function writeUInt16(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xffff); + } + + if (isBigEndian) { + buffer[offset] = (value & 0xff00) >>> 8; + buffer[offset + 1] = value & 0x00ff; + } else { + buffer[offset + 1] = (value & 0xff00) >>> 8; + buffer[offset] = value & 0x00ff; + } +} + +Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) { + writeUInt16(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) { + writeUInt16(this, value, offset, true, noAssert); +}; + +function writeUInt32(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xffffffff); + } + + if (isBigEndian) { + buffer[offset] = (value >>> 24) & 0xff; + buffer[offset + 1] = (value >>> 16) & 0xff; + buffer[offset + 2] = (value >>> 8) & 0xff; + buffer[offset + 3] = value & 0xff; + } else { + buffer[offset + 3] = (value >>> 24) & 0xff; + buffer[offset + 2] = (value >>> 16) & 0xff; + buffer[offset + 1] = (value >>> 8) & 0xff; + buffer[offset] = value & 0xff; + } +} + +Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) { + writeUInt32(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) { + writeUInt32(this, value, offset, true, noAssert); +}; + + +/* + * We now move onto our friends in the signed number category. Unlike unsigned + * numbers, we're going to have to worry a bit more about how we put values into + * arrays. Since we are only worrying about signed 32-bit values, we're in + * slightly better shape. Unfortunately, we really can't do our favorite binary + * & in this system. It really seems to do the wrong thing. For example: + * + * > -32 & 0xff + * 224 + * + * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of + * this aren't treated as a signed number. Ultimately a bad thing. + * + * What we're going to want to do is basically create the unsigned equivalent of + * our representation and pass that off to the wuint* functions. To do that + * we're going to do the following: + * + * - if the value is positive + * we can pass it directly off to the equivalent wuint + * - if the value is negative + * we do the following computation: + * mb + val + 1, where + * mb is the maximum unsigned value in that byte size + * val is the Javascript negative integer + * + * + * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If + * you do out the computations: + * + * 0xffff - 128 + 1 + * 0xffff - 127 + * 0xff80 + * + * You can then encode this value as the signed version. This is really rather + * hacky, but it should work and get the job done which is our goal here. + */ + +/* + * A series of checks to make sure we actually have a signed 32-bit number + */ +function verifsint(value, max, min) { + assert.ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + assert.ok(value <= max, 'value larger than maximum allowed value'); + + assert.ok(value >= min, 'value smaller than minimum allowed value'); + + assert.ok(Math.floor(value) === value, 'value has a fractional component'); +} + +function verifIEEE754(value, max, min) { + assert.ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + assert.ok(value <= max, 'value larger than maximum allowed value'); + + assert.ok(value >= min, 'value smaller than minimum allowed value'); +} + +Buffer.prototype.writeInt8 = function(value, offset, noAssert) { + var buffer = this; + + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7f, -0x80); + } + + if (value >= 0) { + buffer.writeUInt8(value, offset, noAssert); + } else { + buffer.writeUInt8(0xff + value + 1, offset, noAssert); + } +}; + +function writeInt16(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7fff, -0x8000); + } + + if (value >= 0) { + writeUInt16(buffer, value, offset, isBigEndian, noAssert); + } else { + writeUInt16(buffer, 0xffff + value + 1, offset, isBigEndian, noAssert); + } +} + +Buffer.prototype.writeInt16LE = function(value, offset, noAssert) { + writeInt16(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeInt16BE = function(value, offset, noAssert) { + writeInt16(this, value, offset, true, noAssert); +}; + +function writeInt32(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7fffffff, -0x80000000); + } + + if (value >= 0) { + writeUInt32(buffer, value, offset, isBigEndian, noAssert); + } else { + writeUInt32(buffer, 0xffffffff + value + 1, offset, isBigEndian, noAssert); + } +} + +Buffer.prototype.writeInt32LE = function(value, offset, noAssert) { + writeInt32(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeInt32BE = function(value, offset, noAssert) { + writeInt32(this, value, offset, true, noAssert); +}; + +function writeFloat(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to write beyond buffer length'); + + verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38); + } + + require('buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian, + 23, 4); +} + +Buffer.prototype.writeFloatLE = function(value, offset, noAssert) { + writeFloat(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeFloatBE = function(value, offset, noAssert) { + writeFloat(this, value, offset, true, noAssert); +}; + +function writeDouble(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 7 < buffer.length, + 'Trying to write beyond buffer length'); + + verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308); + } + + require('buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian, + 52, 8); +} + +Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) { + writeDouble(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) { + writeDouble(this, value, offset, true, noAssert); +}; + +SlowBuffer.prototype.readUInt8 = Buffer.prototype.readUInt8; +SlowBuffer.prototype.readUInt16LE = Buffer.prototype.readUInt16LE; +SlowBuffer.prototype.readUInt16BE = Buffer.prototype.readUInt16BE; +SlowBuffer.prototype.readUInt32LE = Buffer.prototype.readUInt32LE; +SlowBuffer.prototype.readUInt32BE = Buffer.prototype.readUInt32BE; +SlowBuffer.prototype.readInt8 = Buffer.prototype.readInt8; +SlowBuffer.prototype.readInt16LE = Buffer.prototype.readInt16LE; +SlowBuffer.prototype.readInt16BE = Buffer.prototype.readInt16BE; +SlowBuffer.prototype.readInt32LE = Buffer.prototype.readInt32LE; +SlowBuffer.prototype.readInt32BE = Buffer.prototype.readInt32BE; +SlowBuffer.prototype.readFloatLE = Buffer.prototype.readFloatLE; +SlowBuffer.prototype.readFloatBE = Buffer.prototype.readFloatBE; +SlowBuffer.prototype.readDoubleLE = Buffer.prototype.readDoubleLE; +SlowBuffer.prototype.readDoubleBE = Buffer.prototype.readDoubleBE; +SlowBuffer.prototype.writeUInt8 = Buffer.prototype.writeUInt8; +SlowBuffer.prototype.writeUInt16LE = Buffer.prototype.writeUInt16LE; +SlowBuffer.prototype.writeUInt16BE = Buffer.prototype.writeUInt16BE; +SlowBuffer.prototype.writeUInt32LE = Buffer.prototype.writeUInt32LE; +SlowBuffer.prototype.writeUInt32BE = Buffer.prototype.writeUInt32BE; +SlowBuffer.prototype.writeInt8 = Buffer.prototype.writeInt8; +SlowBuffer.prototype.writeInt16LE = Buffer.prototype.writeInt16LE; +SlowBuffer.prototype.writeInt16BE = Buffer.prototype.writeInt16BE; +SlowBuffer.prototype.writeInt32LE = Buffer.prototype.writeInt32LE; +SlowBuffer.prototype.writeInt32BE = Buffer.prototype.writeInt32BE; +SlowBuffer.prototype.writeFloatLE = Buffer.prototype.writeFloatLE; +SlowBuffer.prototype.writeFloatBE = Buffer.prototype.writeFloatBE; +SlowBuffer.prototype.writeDoubleLE = Buffer.prototype.writeDoubleLE; +SlowBuffer.prototype.writeDoubleBE = Buffer.prototype.writeDoubleBE; diff --git a/buildin/web_modules/buffer_ieee754.js b/buildin/web_modules/buffer_ieee754.js new file mode 100644 index 000000000..fb295c0ab --- /dev/null +++ b/buildin/web_modules/buffer_ieee754.js @@ -0,0 +1,84 @@ +exports.readIEEE754 = function(buffer, offset, isBE, mLen, nBytes) { + var e, m, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + nBits = -7, + i = isBE ? 0 : (nBytes - 1), + d = isBE ? 1 : -1, + s = buffer[offset + i]; + + i += d; + + e = s & ((1 << (-nBits)) - 1); + s >>= (-nBits); + nBits += eLen; + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8); + + m = e & ((1 << (-nBits)) - 1); + e >>= (-nBits); + nBits += mLen; + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8); + + if (e === 0) { + e = 1 - eBias; + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity); + } else { + m = m + Math.pow(2, mLen); + e = e - eBias; + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen); +}; + +exports.writeIEEE754 = function(buffer, value, offset, isBE, mLen, nBytes) { + var e, m, c, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), + i = isBE ? (nBytes - 1) : 0, + d = isBE ? -1 : 1, + s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; + + value = Math.abs(value); + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0; + e = eMax; + } else { + e = Math.floor(Math.log(value) / Math.LN2); + if (value * (c = Math.pow(2, -e)) < 1) { + e--; + c *= 2; + } + if (e + eBias >= 1) { + value += rt / c; + } else { + value += rt * Math.pow(2, 1 - eBias); + } + if (value * c >= 2) { + e++; + c /= 2; + } + + if (e + eBias >= eMax) { + m = 0; + e = eMax; + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen); + e = e + eBias; + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); + e = 0; + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8); + + e = (e << mLen) | m; + eLen += mLen; + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8); + + buffer[offset + i - d] |= s * 128; +}; diff --git a/buildin/web_modules/events.js b/buildin/web_modules/events.js index 1fff1e361..b42f086ee 100644 --- a/buildin/web_modules/events.js +++ b/buildin/web_modules/events.js @@ -186,8 +186,6 @@ EventEmitter.prototype.removeListener = function(type, listener) { if (position < 0) return this; list.splice(position, 1); - if (list.length == 0) - delete this._events[type]; } else if (list === listener || (list.listener && list.listener === listener)) { @@ -203,8 +201,15 @@ EventEmitter.prototype.removeAllListeners = function(type) { return this; } - // does not use listeners(), so no side effect of creating _events[type] - if (type && this._events && this._events[type]) this._events[type] = null; + var events = this._events && this._events[type]; + if (!events) return this; + + if (isArray(events)) { + events.splice(0); + } else { + this._events[type] = null; + } + return this; }; diff --git a/buildin/web_modules/path.js b/buildin/web_modules/path.js new file mode 100644 index 000000000..751a2a578 --- /dev/null +++ b/buildin/web_modules/path.js @@ -0,0 +1,450 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var isWindows = process.platform === 'win32'; +var _deprecationWarning = require('util')._deprecationWarning; + + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last == '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + + +if (isWindows) { + // Regex to split a windows path into three parts: [*, device, slash, + // tail] windows-only + var splitDeviceRe = + /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?([\s\S]*?)$/; + + // Regex to split the tail part of the above into [*, dir, basename, ext] + var splitTailRe = + /^([\s\S]+[\\\/](?!$)|[\\\/])?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/\\]*)?)$/; + + // Function to split a filename into [root, dir, basename, ext] + // windows version + var splitPath = function(filename) { + // Separate device+slash from tail + var result = splitDeviceRe.exec(filename), + device = (result[1] || '') + (result[2] || ''), + tail = result[3] || ''; + // Split the tail into dir, basename and extension + var result2 = splitTailRe.exec(tail), + dir = result2[1] || '', + basename = result2[2] || '', + ext = result2[3] || ''; + return [device, dir, basename, ext]; + }; + + // path.resolve([from ...], to) + // windows version + exports.resolve = function() { + var resolvedDevice = '', + resolvedTail = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1; i--) { + var path; + if (i >= 0) { + path = arguments[i]; + } else if (!resolvedDevice) { + path = process.cwd(); + } else { + // Windows has the concept of drive-specific current working + // directories. If we've resolved a drive letter but not yet an + // absolute path, get cwd for that drive. We're sure the device is not + // an unc path at this points, because unc paths are always absolute. + path = process.env['=' + resolvedDevice]; + // Verify that a drive-local cwd was found and that it actually points + // to our drive. If not, default to the drive's root. + if (!path || path.slice(0, 3).toLowerCase() !== + resolvedDevice.toLowerCase() + '\\') { + path = resolvedDevice + '\\'; + } + } + + // Skip empty and invalid entries + if (typeof path !== 'string' || !path) { + continue; + } + + var result = splitDeviceRe.exec(path), + device = result[1] || '', + isUnc = device && device.charAt(1) !== ':', + isAbsolute = !!result[2] || isUnc, // UNC paths are always absolute + tail = result[3]; + + if (device && + resolvedDevice && + device.toLowerCase() !== resolvedDevice.toLowerCase()) { + // This path points to another device so it is not applicable + continue; + } + + if (!resolvedDevice) { + resolvedDevice = device; + } + if (!resolvedAbsolute) { + resolvedTail = tail + '\\' + resolvedTail; + resolvedAbsolute = isAbsolute; + } + + if (resolvedDevice && resolvedAbsolute) { + break; + } + } + + // Replace slashes (in UNC share name) by backslashes + resolvedDevice = resolvedDevice.replace(/\//g, '\\'); + + // At this point the path should be resolved to a full absolute path, + // but handle relative paths to be safe (might happen when process.cwd() + // fails) + + // Normalize the tail path + + function f(p) { + return !!p; + } + + resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/).filter(f), + !resolvedAbsolute).join('\\'); + + return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) || + '.'; + }; + + // windows version + exports.normalize = function(path) { + var result = splitDeviceRe.exec(path), + device = result[1] || '', + isUnc = device && device.charAt(1) !== ':', + isAbsolute = !!result[2] || isUnc, // UNC paths are always absolute + tail = result[3], + trailingSlash = /[\\\/]$/.test(tail); + + // Normalize the tail path + tail = normalizeArray(tail.split(/[\\\/]+/).filter(function(p) { + return !!p; + }), !isAbsolute).join('\\'); + + if (!tail && !isAbsolute) { + tail = '.'; + } + if (tail && trailingSlash) { + tail += '\\'; + } + + return device + (isAbsolute ? '\\' : '') + tail; + }; + + // windows version + exports.join = function() { + function f(p) { + return p && typeof p === 'string'; + } + + var paths = Array.prototype.slice.call(arguments, 0).filter(f); + var joined = paths.join('\\'); + + // Make sure that the joined path doesn't start with two slashes + // - it will be mistaken for an unc path by normalize() - + // unless the paths[0] also starts with two slashes + if (/^[\\\/]{2}/.test(joined) && !/^[\\\/]{2}/.test(paths[0])) { + joined = joined.slice(1); + } + + return exports.normalize(joined); + }; + + // path.relative(from, to) + // it will solve the relative path from 'from' to 'to', for instance: + // from = 'C:\\orandea\\test\\aaa' + // to = 'C:\\orandea\\impl\\bbb' + // The output of the function should be: '..\\..\\impl\\bbb' + // windows version + exports.relative = function(from, to) { + from = exports.resolve(from); + to = exports.resolve(to); + + // windows is not case sensitive + var lowerFrom = from.toLowerCase(); + var lowerTo = to.toLowerCase(); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var toParts = trim(to.split('\\')); + + var lowerFromParts = trim(lowerFrom.split('\\')); + var lowerToParts = trim(lowerTo.split('\\')); + + var length = Math.min(lowerFromParts.length, lowerToParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (lowerFromParts[i] !== lowerToParts[i]) { + samePartsLength = i; + break; + } + } + + if (samePartsLength == 0) { + return to; + } + + var outputParts = []; + for (var i = samePartsLength; i < lowerFromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('\\'); + }; + + +} else /* posix */ { + + // Split a filename into [root, dir, basename, ext], unix version + // 'root' is just a slash, or nothing. + var splitPathRe = + /^(\/?)([\s\S]+\/(?!$)|\/)?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/]*)?)$/; + var splitPath = function(filename) { + var result = splitPathRe.exec(filename); + return [result[1] || '', result[2] || '', result[3] || '', result[4] || '']; + }; + + // path.resolve([from ...], to) + // posix version + exports.resolve = function() { + var resolvedPath = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? arguments[i] : process.cwd(); + + // Skip empty and invalid entries + if (typeof path !== 'string' || !path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; + }; + + // path.normalize(path) + // posix version + exports.normalize = function(path) { + var isAbsolute = path.charAt(0) === '/', + trailingSlash = path.slice(-1) === '/'; + + // Normalize the path + path = normalizeArray(path.split('/').filter(function(p) { + return !!p; + }), !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isAbsolute ? '/' : '') + path; + }; + + + // posix version + exports.join = function() { + var paths = Array.prototype.slice.call(arguments, 0); + return exports.normalize(paths.filter(function(p, index) { + return p && typeof p === 'string'; + }).join('/')); + }; + + + // path.relative(from, to) + // posix version + exports.relative = function(from, to) { + from = exports.resolve(from).substr(1); + to = exports.resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); + }; + +} + + +exports.dirname = function(path) { + var result = splitPath(path), + root = result[0], + dir = result[1]; + + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substring(0, dir.length - 1); + } + + return root + dir; +}; + + +exports.basename = function(path, ext) { + var f = splitPath(path)[2]; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; +}; + + +exports.extname = function(path) { + return splitPath(path)[3]; +}; + +/* +exports.exists = function(path, callback) { + require('fs').exists(path, callback); +}; +module.deprecate('exists', 'It is now called `fs.exists`.'); + + +exports.existsSync = function(path) { + return require('fs').existsSync(path); +}; +module.deprecate('existsSync', 'It is now called `fs.existsSync`.'); +*/ + +if (isWindows) { + exports._makeLong = function(path) { + path = '' + path; + if (!path) { + return ''; + } + + var resolvedPath = exports.resolve(path); + + if (resolvedPath.match(/^[a-zA-Z]\:\\/)) { + // path is local filesystem path, which needs to be converted + // to long UNC path. + return '\\\\?\\' + resolvedPath; + } else if (resolvedPath.match(/^\\\\[^?.]/)) { + // path is network UNC path, which needs to be converted + // to long UNC path. + return '\\\\?\\UNC\\' + resolvedPath.substring(2); + } + + return path; + }; +} else { + exports._makeLong = function(path) { + return path; + }; +} diff --git a/buildin/web_modules/punycode.js b/buildin/web_modules/punycode.js new file mode 100644 index 000000000..eae76dcc0 --- /dev/null +++ b/buildin/web_modules/punycode.js @@ -0,0 +1,512 @@ +/*! http://mths.be/punycode by @mathias */ +;(function(root) { + + /** + * The `punycode` object. + * @name punycode + * @type Object + */ + var punycode, + + /** Detect free variables `define`, `exports`, `module` and `require` */ + freeDefine = typeof define == 'function' && typeof define.amd == 'object' && + define.amd && define, + freeExports = typeof exports == 'object' && exports, + freeModule = typeof module == 'object' && module, + // freeRequire = typeof require == 'function' && require, + + /** Highest positive signed 32-bit float value */ + maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 + + /** Bootstring parameters */ + base = 36, + tMin = 1, + tMax = 26, + skew = 38, + damp = 700, + initialBias = 72, + initialN = 128, // 0x80 + delimiter = '-', // '\x2D' + + /** Regular expressions */ + regexNonASCII = /[^ -~]/, // unprintable ASCII chars + non-ASCII chars + regexPunycode = /^xn--/, + + /** Error messages */ + errors = { + 'overflow': 'Overflow: input needs wider integers to process.', + 'ucs2decode': 'UCS-2(decode): illegal sequence', + 'ucs2encode': 'UCS-2(encode): illegal value', + 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', + 'invalid-input': 'Invalid input' + }, + + /** Convenience shortcuts */ + baseMinusTMin = base - tMin, + floor = Math.floor, + stringFromCharCode = String.fromCharCode, + + /** Temporary variable */ + key; + + /*--------------------------------------------------------------------------*/ + + /** + * A generic error utility function. + * @private + * @param {String} type The error type. + * @returns {Error} Throws a `RangeError` with the applicable error message. + */ + function error(type) { + throw RangeError(errors[type]); + } + + /** + * A generic `Array#map` utility function. + * @private + * @param {Array} array The array to iterate over. + * @param {Function} callback The function that gets called for every array + * item. + * @returns {Array} A new array of values returned by the callback function. + */ + function map(array, fn) { + var length = array.length; + while (length--) { + array[length] = fn(array[length]); + } + return array; + } + + /** + * A simple `Array#map`-like wrapper to work with domain name strings. + * @private + * @param {String} domain The domain name. + * @param {Function} callback The function that gets called for every + * character. + * @returns {Array} A new string of characters returned by the callback + * function. + */ + function mapDomain(string, fn) { + var glue = '.'; + return map(string.split(glue), fn).join(glue); + } + + /** + * Creates an array containing the decimal code points of each Unicode + * character in the string. While JavaScript uses UCS-2 internally, + * this function will convert a pair of surrogate halves (each of which + * UCS-2 exposes as separate characters) into a single code point, + * matching UTF-16. + * @see `punycode.ucs2.encode` + * @see + * @memberOf punycode.ucs2 + * @name decode + * @param {String} string The Unicode input string (UCS-2). + * @returns {Array} The new array of code points. + */ + function ucs2decode(string) { + var output = [], + counter = 0, + length = string.length, + value, + extra; + while (counter < length) { + value = string.charCodeAt(counter++); + if ((value & 0xF800) == 0xD800) { + extra = string.charCodeAt(counter++); + if ((value & 0xFC00) != 0xD800 || (extra & 0xFC00) != 0xDC00) { + error('ucs2decode'); + } + value = ((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000; + } + output.push(value); + } + return output; + } + + /** + * Creates a string based on an array of decimal code points. + * @see `punycode.ucs2.decode` + * @memberOf punycode.ucs2 + * @name encode + * @param {Array} codePoints The array of decimal code points. + * @returns {String} The new Unicode string (UCS-2). + */ + function ucs2encode(array) { + return map(array, function(value) { + var output = ''; + if ((value & 0xF800) == 0xD800) { + error('ucs2encode'); + } + if (value > 0xFFFF) { + value -= 0x10000; + output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); + value = 0xDC00 | value & 0x3FF; + } + output += stringFromCharCode(value); + return output; + }).join(''); + } + + /** + * Converts a basic code point into a digit/integer. + * @see `digitToBasic()` + * @private + * @param {Number} codePoint The basic (decimal) code point. + * @returns {Number} The numeric value of a basic code point (for use in + * representing integers) in the range `0` to `base - 1`, or `base` if + * the code point does not represent a value. + */ + function basicToDigit(codePoint) { + return codePoint - 48 < 10 + ? codePoint - 22 + : codePoint - 65 < 26 + ? codePoint - 65 + : codePoint - 97 < 26 + ? codePoint - 97 + : base; + } + + /** + * Converts a digit/integer into a basic code point. + * @see `basicToDigit()` + * @private + * @param {Number} digit The numeric value of a basic code point. + * @returns {Number} The basic code point whose value (when used for + * representing integers) is `digit`, which needs to be in the range + * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is + * used; else, the lowercase form is used. The behavior is undefined + * if flag is non-zero and `digit` has no uppercase form. + */ + function digitToBasic(digit, flag) { + // 0..25 map to ASCII a..z or A..Z + // 26..35 map to ASCII 0..9 + return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); + } + + /** + * Bias adaptation function as per section 3.4 of RFC 3492. + * http://tools.ietf.org/html/rfc3492#section-3.4 + * @private + */ + function adapt(delta, numPoints, firstTime) { + var k = 0; + delta = firstTime ? floor(delta / damp) : delta >> 1; + delta += floor(delta / numPoints); + for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { + delta = floor(delta / baseMinusTMin); + } + return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); + } + + /** + * Converts a basic code point to lowercase is `flag` is falsy, or to + * uppercase if `flag` is truthy. The code point is unchanged if it's + * caseless. The behavior is undefined if `codePoint` is not a basic code + * point. + * @private + * @param {Number} codePoint The numeric value of a basic code point. + * @returns {Number} The resulting basic code point. + */ + function encodeBasic(codePoint, flag) { + codePoint -= (codePoint - 97 < 26) << 5; + return codePoint + (!flag && codePoint - 65 < 26) << 5; + } + + /** + * Converts a Punycode string of ASCII code points to a string of Unicode + * code points. + * @memberOf punycode + * @param {String} input The Punycode string of ASCII code points. + * @returns {String} The resulting string of Unicode code points. + */ + function decode(input) { + // Don't use UCS-2 + var output = [], + inputLength = input.length, + out, + i = 0, + n = initialN, + bias = initialBias, + basic, + j, + index, + oldi, + w, + k, + digit, + t, + length, + /** Cached calculation results */ + baseMinusT; + + // Handle the basic code points: let `basic` be the number of input code + // points before the last delimiter, or `0` if there is none, then copy + // the first basic code points to the output. + + basic = input.lastIndexOf(delimiter); + if (basic < 0) { + basic = 0; + } + + for (j = 0; j < basic; ++j) { + // if it's not a basic code point + if (input.charCodeAt(j) >= 0x80) { + error('not-basic'); + } + output.push(input.charCodeAt(j)); + } + + // Main decoding loop: start just after the last delimiter if any basic code + // points were copied; start at the beginning otherwise. + + for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { + + // `index` is the index of the next character to be consumed. + // Decode a generalized variable-length integer into `delta`, + // which gets added to `i`. The overflow checking is easier + // if we increase `i` as we go, then subtract off its starting + // value at the end to obtain `delta`. + for (oldi = i, w = 1, k = base; /* no condition */; k += base) { + + if (index >= inputLength) { + error('invalid-input'); + } + + digit = basicToDigit(input.charCodeAt(index++)); + + if (digit >= base || digit > floor((maxInt - i) / w)) { + error('overflow'); + } + + i += digit * w; + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + + if (digit < t) { + break; + } + + baseMinusT = base - t; + if (w > floor(maxInt / baseMinusT)) { + error('overflow'); + } + + w *= baseMinusT; + + } + + out = output.length + 1; + bias = adapt(i - oldi, out, oldi == 0); + + // `i` was supposed to wrap around from `out` to `0`, + // incrementing `n` each time, so we'll fix that now: + if (floor(i / out) > maxInt - n) { + error('overflow'); + } + + n += floor(i / out); + i %= out; + + // Insert `n` at position `i` of the output + output.splice(i++, 0, n); + + } + + return ucs2encode(output); + } + + /** + * Converts a string of Unicode code points to a Punycode string of ASCII + * code points. + * @memberOf punycode + * @param {String} input The string of Unicode code points. + * @returns {String} The resulting Punycode string of ASCII code points. + */ + function encode(input) { + var n, + delta, + handledCPCount, + basicLength, + bias, + j, + m, + q, + k, + t, + currentValue, + output = [], + /** `inputLength` will hold the number of code points in `input`. */ + inputLength, + /** Cached calculation results */ + handledCPCountPlusOne, + baseMinusT, + qMinusT; + + // Convert the input in UCS-2 to Unicode + input = ucs2decode(input); + + // Cache the length + inputLength = input.length; + + // Initialize the state + n = initialN; + delta = 0; + bias = initialBias; + + // Handle the basic code points + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue < 0x80) { + output.push(stringFromCharCode(currentValue)); + } + } + + handledCPCount = basicLength = output.length; + + // `handledCPCount` is the number of code points that have been handled; + // `basicLength` is the number of basic code points. + + // Finish the basic string - if it is not empty - with a delimiter + if (basicLength) { + output.push(delimiter); + } + + // Main encoding loop: + while (handledCPCount < inputLength) { + + // All non-basic code points < n have been handled already. Find the next + // larger one: + for (m = maxInt, j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue >= n && currentValue < m) { + m = currentValue; + } + } + + // Increase `delta` enough to advance the decoder's state to , + // but guard against overflow + handledCPCountPlusOne = handledCPCount + 1; + if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { + error('overflow'); + } + + delta += (m - n) * handledCPCountPlusOne; + n = m; + + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + + if (currentValue < n && ++delta > maxInt) { + error('overflow'); + } + + if (currentValue == n) { + // Represent delta as a generalized variable-length integer + for (q = delta, k = base; /* no condition */; k += base) { + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + if (q < t) { + break; + } + qMinusT = q - t; + baseMinusT = base - t; + output.push( + stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) + ); + q = floor(qMinusT / baseMinusT); + } + + output.push(stringFromCharCode(digitToBasic(q, 0))); + bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); + delta = 0; + ++handledCPCount; + } + } + + ++delta; + ++n; + + } + return output.join(''); + } + + /** + * Converts a Punycode string representing a domain name to Unicode. Only the + * Punycoded parts of the domain name will be converted, i.e. it doesn't + * matter if you call it on a string that has already been converted to + * Unicode. + * @memberOf punycode + * @param {String} domain The Punycode domain name to convert to Unicode. + * @returns {String} The Unicode representation of the given Punycode + * string. + */ + function toUnicode(domain) { + return mapDomain(domain, function(string) { + return regexPunycode.test(string) + ? decode(string.slice(4).toLowerCase()) + : string; + }); + } + + /** + * Converts a Unicode string representing a domain name to Punycode. Only the + * non-ASCII parts of the domain name will be converted, i.e. it doesn't + * matter if you call it with a domain that's already in ASCII. + * @memberOf punycode + * @param {String} domain The domain name to convert, as a Unicode string. + * @returns {String} The Punycode representation of the given domain name. + */ + function toASCII(domain) { + return mapDomain(domain, function(string) { + return regexNonASCII.test(string) + ? 'xn--' + encode(string) + : string; + }); + } + + /*--------------------------------------------------------------------------*/ + + /** Define the public API */ + punycode = { + /** + * A string representing the current Punycode.js version number. + * @memberOf punycode + * @type String + */ + 'version': '1.0.0', + /** + * An object of methods to convert from JavaScript's internal character + * representation (UCS-2) to decimal Unicode code points, and back. + * @see + * @memberOf punycode + * @type Object + */ + 'ucs2': { + 'decode': ucs2decode, + 'encode': ucs2encode + }, + 'decode': decode, + 'encode': encode, + 'toASCII': toASCII, + 'toUnicode': toUnicode + }; + + /** Expose `punycode` */ + if (freeExports) { + if (freeModule && freeModule.exports == freeExports) { + // in Node.js or Ringo 0.8+ + freeModule.exports = punycode; + } else { + // in Narwhal or Ringo 0.7- + for (key in punycode) { + punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]); + } + } + } else if (freeDefine) { + // via curl.js or RequireJS + define('punycode', punycode); + } else { + // in a browser or Rhino + root.punycode = punycode; + } + +}(this)); diff --git a/buildin/web_modules/querystring.js b/buildin/web_modules/querystring.js new file mode 100644 index 000000000..6633dd279 --- /dev/null +++ b/buildin/web_modules/querystring.js @@ -0,0 +1,142 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Query String Utilities + +var QueryString = exports; + + +// If obj.hasOwnProperty has been overridden, then calling +// obj.hasOwnProperty(prop) will break. +// See: https://github.com/joyent/node/issues/1707 +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + + +function charCode(c) { + return c.charCodeAt(0); +} + + +QueryString.unescape = function(s, decodeSpaces) { + return decodeURIComponent(s, decodeSpaces) +}; + + +QueryString.escape = function(str) { + return encodeURIComponent(str); +}; + +var stringifyPrimitive = function(v) { + switch (typeof v) { + case 'string': + return v; + + case 'boolean': + return v ? 'true' : 'false'; + + case 'number': + return isFinite(v) ? v : ''; + + default: + return ''; + } +}; + + +QueryString.stringify = QueryString.encode = function(obj, sep, eq, name) { + sep = sep || '&'; + eq = eq || '='; + obj = (obj === null) ? undefined : obj; + + switch (typeof obj) { + case 'object': + return Object.keys(obj).map(function(k) { + if (Array.isArray(obj[k])) { + return obj[k].map(function(v) { + return QueryString.escape(stringifyPrimitive(k)) + + eq + + QueryString.escape(stringifyPrimitive(v)); + }).join(sep); + } else { + return QueryString.escape(stringifyPrimitive(k)) + + eq + + QueryString.escape(stringifyPrimitive(obj[k])); + } + }).join(sep); + + default: + if (!name) return ''; + return QueryString.escape(stringifyPrimitive(name)) + eq + + QueryString.escape(stringifyPrimitive(obj)); + } +}; + +// Parse a key=val string. +QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { + sep = sep || '&'; + eq = eq || '='; + var obj = {}, + maxKeys = 1000; + + // Handle maxKeys = 0 case + if (options && typeof options.maxKeys === 'number') { + maxKeys = options.maxKeys; + } + + if (typeof qs !== 'string' || qs.length === 0) { + return obj; + } + + var regexp = /\+/g; + qs = qs.split(sep); + + // maxKeys <= 0 means that we should not limit keys count + if (maxKeys > 0) { + qs = qs.slice(0, maxKeys); + } + + for (var i = 0, len = qs.length; i < len; ++i) { + var x = qs[i].replace(regexp, '%20'), + idx = x.indexOf(eq), + kstr = x.substring(0, idx), + vstr = x.substring(idx + 1), k, v; + + try { + k = decodeURIComponent(kstr); + v = decodeURIComponent(vstr); + } catch (e) { + k = QueryString.unescape(kstr, true); + v = QueryString.unescape(vstr, true); + } + + if (!hasOwnProperty(obj, k)) { + obj[k] = v; + } else if (!Array.isArray(obj[k])) { + obj[k] = [obj[k], v]; + } else { + obj[k].push(v); + } + } + + return obj; +}; diff --git a/buildin/web_modules/url.js b/buildin/web_modules/url.js new file mode 100644 index 000000000..31eb536e1 --- /dev/null +++ b/buildin/web_modules/url.js @@ -0,0 +1,635 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var punycode = require('punycode'); + +exports.parse = urlParse; +exports.resolve = urlResolve; +exports.resolveObject = urlResolveObject; +exports.format = urlFormat; + +// Reference: RFC 3986, RFC 1808, RFC 2396 + +// define these here so at least they only have to be +// compiled once on the first module load. +var protocolPattern = /^([a-z0-9.+-]+:)/i, + portPattern = /:[0-9]*$/, + // RFC 2396: characters reserved for delimiting URLs. + delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'], + // RFC 2396: characters not allowed for various reasons. + unwise = ['{', '}', '|', '\\', '^', '~', '`'].concat(delims), + // Allowed by RFCs, but cause of XSS attacks. Always escape these. + autoEscape = ['\''], + // Characters that are never ever allowed in a hostname. + // Note that any invalid chars are also handled, but these + // are the ones that are *expected* to be seen, so we fast-path + // them. + nonHostChars = ['%', '/', '?', ';', '#'] + .concat(unwise).concat(autoEscape), + nonAuthChars = ['/', '@', '?', '#'].concat(delims), + hostnameMaxLen = 255, + hostnamePartPattern = /^[a-zA-Z0-9][a-z0-9A-Z_-]{0,62}$/, + hostnamePartStart = /^([a-zA-Z0-9][a-z0-9A-Z_-]{0,62})(.*)$/, + // protocols that can allow "unsafe" and "unwise" chars. + unsafeProtocol = { + 'javascript': true, + 'javascript:': true + }, + // protocols that never have a hostname. + hostlessProtocol = { + 'javascript': true, + 'javascript:': true + }, + // protocols that always have a path component. + pathedProtocol = { + 'http': true, + 'https': true, + 'ftp': true, + 'gopher': true, + 'file': true, + 'http:': true, + 'ftp:': true, + 'gopher:': true, + 'file:': true + }, + // protocols that always contain a // bit. + slashedProtocol = { + 'http': true, + 'https': true, + 'ftp': true, + 'gopher': true, + 'file': true, + 'http:': true, + 'https:': true, + 'ftp:': true, + 'gopher:': true, + 'file:': true + }, + querystring = require('querystring'); + +function urlParse(url, parseQueryString, slashesDenoteHost) { + if (url && typeof(url) === 'object' && url.href) return url; + + if (typeof url !== 'string') { + throw new TypeError("Parameter 'url' must be a string, not " + typeof url); + } + + var out = {}, + rest = url; + + // cut off any delimiters. + // This is to support parse stuff like "" + for (var i = 0, l = rest.length; i < l; i++) { + if (delims.indexOf(rest.charAt(i)) === -1) break; + } + if (i !== 0) rest = rest.substr(i); + + + var proto = protocolPattern.exec(rest); + if (proto) { + proto = proto[0]; + var lowerProto = proto.toLowerCase(); + out.protocol = lowerProto; + rest = rest.substr(proto.length); + } + + // figure out if it's got a host + // user@server is *always* interpreted as a hostname, and url + // resolution will treat //foo/bar as host=foo,path=bar because that's + // how the browser resolves relative URLs. + if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) { + var slashes = rest.substr(0, 2) === '//'; + if (slashes && !(proto && hostlessProtocol[proto])) { + rest = rest.substr(2); + out.slashes = true; + } + } + + if (!hostlessProtocol[proto] && + (slashes || (proto && !slashedProtocol[proto]))) { + // there's a hostname. + // the first instance of /, ?, ;, or # ends the host. + // don't enforce full RFC correctness, just be unstupid about it. + + // If there is an @ in the hostname, then non-host chars *are* allowed + // to the left of the first @ sign, unless some non-auth character + // comes *before* the @-sign. + // URLs are obnoxious. + var atSign = rest.indexOf('@'); + if (atSign !== -1) { + var auth = rest.slice(0, atSign); + + // there *may be* an auth + var hasAuth = true; + for (var i = 0, l = nonAuthChars.length; i < l; i++) { + if (auth.indexOf(nonAuthChars[i]) !== -1) { + // not a valid auth. Something like http://foo.com/bar@baz/ + hasAuth = false; + break; + } + } + + if (hasAuth) { + // pluck off the auth portion. + out.auth = decodeURIComponent(auth); + rest = rest.substr(atSign + 1); + } + } + + var firstNonHost = -1; + for (var i = 0, l = nonHostChars.length; i < l; i++) { + var index = rest.indexOf(nonHostChars[i]); + if (index !== -1 && + (firstNonHost < 0 || index < firstNonHost)) firstNonHost = index; + } + + if (firstNonHost !== -1) { + out.host = rest.substr(0, firstNonHost); + rest = rest.substr(firstNonHost); + } else { + out.host = rest; + rest = ''; + } + + // pull out port. + var p = parseHost(out.host); + var keys = Object.keys(p); + for (var i = 0, l = keys.length; i < l; i++) { + var key = keys[i]; + out[key] = p[key]; + } + + // we've indicated that there is a hostname, + // so even if it's empty, it has to be present. + out.hostname = out.hostname || ''; + + // if hostname begins with [ and ends with ] + // assume that it's an IPv6 address. + var ipv6Hostname = out.hostname[0] === '[' && + out.hostname[out.hostname.length - 1] === ']'; + + // validate a little. + if (out.hostname.length > hostnameMaxLen) { + out.hostname = ''; + } else if (!ipv6Hostname) { + var hostparts = out.hostname.split(/\./); + for (var i = 0, l = hostparts.length; i < l; i++) { + var part = hostparts[i]; + if (!part) continue; + if (!part.match(hostnamePartPattern)) { + var newpart = ''; + for (var j = 0, k = part.length; j < k; j++) { + if (part.charCodeAt(j) > 127) { + // we replace non-ASCII char with a temporary placeholder + // we need this to make sure size of hostname is not + // broken by replacing non-ASCII by nothing + newpart += 'x'; + } else { + newpart += part[j]; + } + } + // we test again with ASCII char only + if (!newpart.match(hostnamePartPattern)) { + var validParts = hostparts.slice(0, i); + var notHost = hostparts.slice(i + 1); + var bit = part.match(hostnamePartStart); + if (bit) { + validParts.push(bit[1]); + notHost.unshift(bit[2]); + } + if (notHost.length) { + rest = '/' + notHost.join('.') + rest; + } + out.hostname = validParts.join('.'); + break; + } + } + } + } + + // hostnames are always lower case. + out.hostname = out.hostname.toLowerCase(); + + if (!ipv6Hostname) { + // IDNA Support: Returns a puny coded representation of "domain". + // It only converts the part of the domain name that + // has non ASCII characters. I.e. it dosent matter if + // you call it with a domain that already is in ASCII. + var domainArray = out.hostname.split('.'); + var newOut = []; + for (var i = 0; i < domainArray.length; ++i) { + var s = domainArray[i]; + newOut.push(s.match(/[^A-Za-z0-9_-]/) ? + 'xn--' + punycode.encode(s) : s); + } + out.hostname = newOut.join('.'); + } + + out.host = (out.hostname || '') + + ((out.port) ? ':' + out.port : ''); + out.href += out.host; + + // strip [ and ] from the hostname + if (ipv6Hostname) { + out.hostname = out.hostname.substr(1, out.hostname.length - 2); + if (rest[0] !== '/') { + rest = '/' + rest; + } + } + } + + // now rest is set to the post-host stuff. + // chop off any delim chars. + if (!unsafeProtocol[lowerProto]) { + + // First, make 100% sure that any "autoEscape" chars get + // escaped, even if encodeURIComponent doesn't think they + // need to be. + for (var i = 0, l = autoEscape.length; i < l; i++) { + var ae = autoEscape[i]; + var esc = encodeURIComponent(ae); + if (esc === ae) { + esc = escape(ae); + } + rest = rest.split(ae).join(esc); + } + + // Now make sure that delims never appear in a url. + var chop = rest.length; + for (var i = 0, l = delims.length; i < l; i++) { + var c = rest.indexOf(delims[i]); + if (c !== -1) { + chop = Math.min(c, chop); + } + } + rest = rest.substr(0, chop); + } + + + // chop off from the tail first. + var hash = rest.indexOf('#'); + if (hash !== -1) { + // got a fragment string. + out.hash = rest.substr(hash); + rest = rest.slice(0, hash); + } + var qm = rest.indexOf('?'); + if (qm !== -1) { + out.search = rest.substr(qm); + out.query = rest.substr(qm + 1); + if (parseQueryString) { + out.query = querystring.parse(out.query); + } + rest = rest.slice(0, qm); + } else if (parseQueryString) { + // no query string, but parseQueryString still requested + out.search = ''; + out.query = {}; + } + if (rest) out.pathname = rest; + if (slashedProtocol[proto] && + out.hostname && !out.pathname) { + out.pathname = '/'; + } + + //to support http.request + if (out.pathname || out.search) { + out.path = (out.pathname ? out.pathname : '') + + (out.search ? out.search : ''); + } + + // finally, reconstruct the href based on what has been validated. + out.href = urlFormat(out); + return out; +} + +// format a parsed object into a url string +function urlFormat(obj) { + // ensure it's an object, and not a string url. + // If it's an obj, this is a no-op. + // this way, you can call url_format() on strings + // to clean up potentially wonky urls. + if (typeof(obj) === 'string') obj = urlParse(obj); + + var auth = obj.auth || ''; + if (auth) { + auth = encodeURIComponent(auth); + auth = auth.replace(/%3A/i, ':'); + auth += '@'; + } + + var protocol = obj.protocol || '', + pathname = obj.pathname || '', + hash = obj.hash || '', + host = false, + query = ''; + + if (obj.host !== undefined) { + host = auth + obj.host; + } else if (obj.hostname !== undefined) { + host = auth + (obj.hostname.indexOf(':') === -1 ? + obj.hostname : + '[' + obj.hostname + ']'); + if (obj.port) { + host += ':' + obj.port; + } + } + + if (obj.query && typeof obj.query === 'object' && + Object.keys(obj.query).length) { + query = querystring.stringify(obj.query); + } + + var search = obj.search || (query && ('?' + query)) || ''; + + if (protocol && protocol.substr(-1) !== ':') protocol += ':'; + + // only the slashedProtocols get the //. Not mailto:, xmpp:, etc. + // unless they had them to begin with. + if (obj.slashes || + (!protocol || slashedProtocol[protocol]) && host !== false) { + host = '//' + (host || ''); + if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname; + } else if (!host) { + host = ''; + } + + if (hash && hash.charAt(0) !== '#') hash = '#' + hash; + if (search && search.charAt(0) !== '?') search = '?' + search; + + return protocol + host + pathname + search + hash; +} + +function urlResolve(source, relative) { + return urlFormat(urlResolveObject(source, relative)); +} + +function urlResolveObject(source, relative) { + if (!source) return relative; + + source = urlParse(urlFormat(source), false, true); + relative = urlParse(urlFormat(relative), false, true); + + // hash is always overridden, no matter what. + source.hash = relative.hash; + + if (relative.href === '') { + source.href = urlFormat(source); + return source; + } + + // hrefs like //foo/bar always cut to the protocol. + if (relative.slashes && !relative.protocol) { + relative.protocol = source.protocol; + //urlParse appends trailing / to urls like http://www.example.com + if (slashedProtocol[relative.protocol] && + relative.hostname && !relative.pathname) { + relative.path = relative.pathname = '/'; + } + relative.href = urlFormat(relative); + return relative; + } + + if (relative.protocol && relative.protocol !== source.protocol) { + // if it's a known url protocol, then changing + // the protocol does weird things + // first, if it's not file:, then we MUST have a host, + // and if there was a path + // to begin with, then we MUST have a path. + // if it is file:, then the host is dropped, + // because that's known to be hostless. + // anything else is assumed to be absolute. + if (!slashedProtocol[relative.protocol]) { + relative.href = urlFormat(relative); + return relative; + } + source.protocol = relative.protocol; + if (!relative.host && !hostlessProtocol[relative.protocol]) { + var relPath = (relative.pathname || '').split('/'); + while (relPath.length && !(relative.host = relPath.shift())); + if (!relative.host) relative.host = ''; + if (!relative.hostname) relative.hostname = ''; + if (relPath[0] !== '') relPath.unshift(''); + if (relPath.length < 2) relPath.unshift(''); + relative.pathname = relPath.join('/'); + } + source.pathname = relative.pathname; + source.search = relative.search; + source.query = relative.query; + source.host = relative.host || ''; + source.auth = relative.auth; + source.hostname = relative.hostname || relative.host; + source.port = relative.port; + //to support http.request + if (source.pathname !== undefined || source.search !== undefined) { + source.path = (source.pathname ? source.pathname : '') + + (source.search ? source.search : ''); + } + source.slashes = source.slashes || relative.slashes; + source.href = urlFormat(source); + return source; + } + + var isSourceAbs = (source.pathname && source.pathname.charAt(0) === '/'), + isRelAbs = ( + relative.host !== undefined || + relative.pathname && relative.pathname.charAt(0) === '/' + ), + mustEndAbs = (isRelAbs || isSourceAbs || + (source.host && relative.pathname)), + removeAllDots = mustEndAbs, + srcPath = source.pathname && source.pathname.split('/') || [], + relPath = relative.pathname && relative.pathname.split('/') || [], + psychotic = source.protocol && + !slashedProtocol[source.protocol]; + + // if the url is a non-slashed url, then relative + // links like ../.. should be able + // to crawl up to the hostname, as well. This is strange. + // source.protocol has already been set by now. + // Later on, put the first path part into the host field. + if (psychotic) { + + delete source.hostname; + delete source.port; + if (source.host) { + if (srcPath[0] === '') srcPath[0] = source.host; + else srcPath.unshift(source.host); + } + delete source.host; + if (relative.protocol) { + delete relative.hostname; + delete relative.port; + if (relative.host) { + if (relPath[0] === '') relPath[0] = relative.host; + else relPath.unshift(relative.host); + } + delete relative.host; + } + mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === ''); + } + + if (isRelAbs) { + // it's absolute. + source.host = (relative.host || relative.host === '') ? + relative.host : source.host; + source.hostname = (relative.hostname || relative.hostname === '') ? + relative.hostname : source.hostname; + source.search = relative.search; + source.query = relative.query; + srcPath = relPath; + // fall through to the dot-handling below. + } else if (relPath.length) { + // it's relative + // throw away the existing file, and take the new path instead. + if (!srcPath) srcPath = []; + srcPath.pop(); + srcPath = srcPath.concat(relPath); + source.search = relative.search; + source.query = relative.query; + } else if ('search' in relative) { + // just pull out the search. + // like href='?foo'. + // Put this after the other two cases because it simplifies the booleans + if (psychotic) { + source.hostname = source.host = srcPath.shift(); + //occationaly the auth can get stuck only in host + //this especialy happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = source.host && source.host.indexOf('@') > 0 ? + source.host.split('@') : false; + if (authInHost) { + source.auth = authInHost.shift(); + source.host = source.hostname = authInHost.shift(); + } + } + source.search = relative.search; + source.query = relative.query; + //to support http.request + if (source.pathname !== undefined || source.search !== undefined) { + source.path = (source.pathname ? source.pathname : '') + + (source.search ? source.search : ''); + } + source.href = urlFormat(source); + return source; + } + if (!srcPath.length) { + // no path at all. easy. + // we've already handled the other stuff above. + delete source.pathname; + //to support http.request + if (!source.search) { + source.path = '/' + source.search; + } else { + delete source.path; + } + source.href = urlFormat(source); + return source; + } + // if a url ENDs in . or .., then it must get a trailing slash. + // however, if it ends in anything else non-slashy, + // then it must NOT get a trailing slash. + var last = srcPath.slice(-1)[0]; + var hasTrailingSlash = ( + (source.host || relative.host) && (last === '.' || last === '..') || + last === ''); + + // strip single dots, resolve double dots to parent dir + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = srcPath.length; i >= 0; i--) { + last = srcPath[i]; + if (last == '.') { + srcPath.splice(i, 1); + } else if (last === '..') { + srcPath.splice(i, 1); + up++; + } else if (up) { + srcPath.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (!mustEndAbs && !removeAllDots) { + for (; up--; up) { + srcPath.unshift('..'); + } + } + + if (mustEndAbs && srcPath[0] !== '' && + (!srcPath[0] || srcPath[0].charAt(0) !== '/')) { + srcPath.unshift(''); + } + + if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) { + srcPath.push(''); + } + + var isAbsolute = srcPath[0] === '' || + (srcPath[0] && srcPath[0].charAt(0) === '/'); + + // put the host back + if (psychotic) { + source.hostname = source.host = isAbsolute ? '' : + srcPath.length ? srcPath.shift() : ''; + //occationaly the auth can get stuck only in host + //this especialy happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = source.host && source.host.indexOf('@') > 0 ? + source.host.split('@') : false; + if (authInHost) { + source.auth = authInHost.shift(); + source.host = source.hostname = authInHost.shift(); + } + } + + mustEndAbs = mustEndAbs || (source.host && srcPath.length); + + if (mustEndAbs && !isAbsolute) { + srcPath.unshift(''); + } + + source.pathname = srcPath.join('/'); + //to support request.http + if (source.pathname !== undefined || source.search !== undefined) { + source.path = (source.pathname ? source.pathname : '') + + (source.search ? source.search : ''); + } + source.auth = relative.auth || source.auth; + source.slashes = source.slashes || relative.slashes; + source.href = urlFormat(source); + return source; +} + +function parseHost(host) { + var out = {}; + var port = portPattern.exec(host); + if (port) { + port = port[0]; + if (port !== ':') { + out.port = port.substr(1); + } + host = host.substr(0, host.length - port.length); + } + if (host) out.hostname = host; + return out; +} diff --git a/examples/build-common.js b/examples/build-common.js new file mode 100644 index 000000000..d9a50fd91 --- /dev/null +++ b/examples/build-common.js @@ -0,0 +1,29 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +require = require("../require-polyfill")(require.valueOf()); + +var cp = require('child_process'); +var tc = require("./template-common"); + +var argv = process.argv; +argv.shift(); +argv.shift(); +var extraArgs = argv.join(" "); + +cp.exec("node ../../bin/webpack.js --verbose "+extraArgs+" example.js js/output.js", function (error, stdout, stderr) { + if(stderr) + console.log(stderr); + if (error !== null) + console.log(error); + var readme = tc(require("raw!"+require("path").join(process.cwd(), "template.md")), require.context("raw!"+process.cwd()), stdout.replace(/[\r\n]*$/, "")); + cp.exec("node ../../bin/webpack.js --verbose --no-colors --min "+extraArgs+" example.js js/output.js", function (error, stdout, stderr) { + if(stderr) + console.log(stderr); + if (error !== null) + console.log(error); + readme = tc(readme, require.context("raw!"+process.cwd()), stdout.replace(/[\r\n]*$/, ""), "min"); + require("fs").writeFile("README.md", readme, "utf-8", function() {}); + }); +}); diff --git a/examples/buildAll.js b/examples/buildAll.js new file mode 100644 index 000000000..0e744fa00 --- /dev/null +++ b/examples/buildAll.js @@ -0,0 +1,11 @@ +var cp = require('child_process'); + +function result(error, stdout, stderr) { + console.log(stderr); +} + +cp.exec("cd code-splitted-require.context && node build.js", result); +cp.exec("cd code-splitting && node build.js", result); +cp.exec("cd coffee-script && node build.js", result); +cp.exec("cd loader && node build.js", result); +cp.exec("cd require.context && node build.js", result); \ No newline at end of file diff --git a/examples/code-splitted-require.context/README.md b/examples/code-splitted-require.context/README.md index 5ca94c32f..4c5d1a0f8 100644 --- a/examples/code-splitted-require.context/README.md +++ b/examples/code-splitted-require.context/README.md @@ -39,6 +39,7 @@ getTemplate("b", function(b) { /******/ var head = document.getElementsByTagName('head')[0]; /******/ var script = document.createElement('script'); /******/ script.type = 'text/javascript'; +/******/ script.charset = 'utf-8'; /******/ script.src = modules.c+chunkId+modules.a; /******/ head.appendChild(script); /******/ } @@ -57,77 +58,38 @@ getTemplate("b", function(b) { /******/({a:".output.js",b:"webpackJsonp",c:"", /******/0: function(module, exports, require) { +/******/ /* WEBPACK FREE VAR INJECTION */ (function(console) { function getTemplate(templateName, callback) { require.ensure(1, function(require) { callback(require(/* ../require.context/templates */1)("./"+templateName)); }); } getTemplate("a", function(a) { - require(/* __webpack_console */2).log(a); + console.log(a); }); getTemplate("b", function(b) { - require(/* __webpack_console */2).log(b); + console.log(b); }); +/******/ /* WEBPACK FREE VAR INJECTION */ }(require(/* __webpack_console */2))) /******/}, /******/ /******/2: function(module, exports, require) { var console = window.console; -exports.log = (console && console.log) || function() {}; -exports.info = (console && console.info) || function() {}; -exports.error = (console && console.error) || function() {}; -exports.warn = (console && console.warn) || function() {}; -exports.dir = (console && console.dir) || function() {}; -exports.time = (console && console.time) || function(label) { +module.exports = console; +for(var name in {log:1, info:1, error:1, warn:1, dir:1, trace:1, assert:1}) + if(!console[name]) + console[name] = function() {}; +if(!console.time) +console.time = function(label) { times[label] = Date.now(); }; -exports.timeEnd = (console && console.timeEnd) || function() { +if(!console.timeEnd) +console.timeEnd = function() { var duration = Date.now() - times[label]; - exports.log('%s: %dms', label, duration); + console.log('%s: %dms', label, duration); }; -exports.trace = (console && console.trace) || function() {}; -exports.assert = (console && console.assert) || function() {}; - -/******/}, -/******/ -/******/}) -``` - -# js/1.output.js - -``` javascript -/******/webpackJsonp(1, { -/******/1: function(module, exports, require) { - -/***/module.exports = function(name) { -/***/ var map = {"./b.js":3,"./a.js":4,"./c.js":5}; -/***/ return require(map[name]||map[name+".web.js"]||map[name+".js"]||name); -/***/}; - -/******/}, -/******/ -/******/3: function(module, exports, require) { - -module.exports = function() { - return "This text was generated by template B"; -} - -/******/}, -/******/ -/******/4: function(module, exports, require) { - -module.exports = function() { - return "This text was generated by template A"; -} - -/******/}, -/******/ -/******/5: function(module, exports, require) { - -module.exports = function() { - return "This text was generated by template C"; -} /******/}, /******/ @@ -144,22 +106,22 @@ Modules: 6 Modules including duplicates: 6 Modules pre chunk: 3 Modules first chunk: 2 - output.js: 2815 characters - 1.output.js: 735 characters + output.js: 2707 characters + 1.output.js: 780 characters output.js - 0 [...]\examples\code-splitted-require.context\example.js + 0 .\example.js main - 2 [...]\buildin\__webpack_console.js - require (2x) from [...]\examples\code-splitted-require.context\example.js + 2 (webpack)\buildin\__webpack_console.js + require (2x) from .\example.js 1.output.js - 1 generated [...]\examples\require.context\templates - async context from [...]\examples\code-splitted-require.context\example.js - 3 [...]\examples\require.context\templates\b.js - async context from [...]\examples\code-splitted-require.context\example.js - 4 [...]\examples\require.context\templates\a.js - async context from [...]\examples\code-splitted-require.context\example.js - 5 [...]\examples\require.context\templates\c.js - async context from [...]\examples\code-splitted-require.context\example.js + 1 [context] ..\require.context\templates + sync context from .\example.js + 3 ..\require.context\templates\a.js + sync context from .\example.js + 4 ..\require.context\templates\b.js + sync context from .\example.js + 5 ..\require.context\templates\c.js + sync context from .\example.js ``` ## Minimized (uglify-js, no zip) @@ -170,20 +132,20 @@ Modules: 6 Modules including duplicates: 6 Modules pre chunk: 3 Modules first chunk: 2 - output.js: 1149 characters - 1.output.js: 407 characters + output.js: 1062 characters + 1.output.js: 436 characters output.js - 0 [...]\examples\code-splitted-require.context\example.js + 0 .\example.js main - 2 [...]\buildin\__webpack_console.js - require (2x) from [...]\examples\code-splitted-require.context\example.js + 2 (webpack)\buildin\__webpack_console.js + require (2x) from .\example.js 1.output.js - 1 generated [...]\examples\require.context\templates - async context from [...]\examples\code-splitted-require.context\example.js - 3 [...]\examples\require.context\templates\a.js - async context from [...]\examples\code-splitted-require.context\example.js - 4 [...]\examples\require.context\templates\c.js - async context from [...]\examples\code-splitted-require.context\example.js - 5 [...]\examples\require.context\templates\b.js - async context from [...]\examples\code-splitted-require.context\example.js + 1 [context] ..\require.context\templates + sync context from .\example.js + 3 ..\require.context\templates\a.js + sync context from .\example.js + 4 ..\require.context\templates\b.js + sync context from .\example.js + 5 ..\require.context\templates\c.js + sync context from .\example.js ``` diff --git a/examples/code-splitted-require.context/build.js b/examples/code-splitted-require.context/build.js index 4fe5c901c..41c29c9d1 100644 --- a/examples/code-splitted-require.context/build.js +++ b/examples/code-splitted-require.context/build.js @@ -1,18 +1 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -var cp = require('child_process'); - -var argv = process.argv; -argv.shift(); -argv.shift(); -var extraArgs = argv.join(" "); - -cp.exec("node ../../bin/webpack.js "+extraArgs+" example.js js/output.js", function (error, stdout, stderr) { - console.log('stdout:\n' + stdout); - console.log('stderr:\n ' + stderr); - if (error !== null) { - console.log('error: ' + error); - } -}); +require("../build-common"); \ No newline at end of file diff --git a/examples/code-splitted-require.context/template.md b/examples/code-splitted-require.context/template.md new file mode 100644 index 000000000..beba68ead --- /dev/null +++ b/examples/code-splitted-require.context/template.md @@ -0,0 +1,25 @@ +# example.js + +``` javascript +{{example.js}} +``` + +# js/output.js + +``` javascript +{{js/output.js}} +``` + +# Info + +## Uncompressed + +``` +{{stdout}} +``` + +## Minimized (uglify-js, no zip) + +``` +{{min:stdout}} +``` diff --git a/examples/code-splitting/README.md b/examples/code-splitting/README.md index 793c66864..1aa80b542 100644 --- a/examples/code-splitting/README.md +++ b/examples/code-splitting/README.md @@ -35,6 +35,7 @@ require.ensure(["c"], function(require) { /******/ var head = document.getElementsByTagName('head')[0]; /******/ var script = document.createElement('script'); /******/ script.type = 'text/javascript'; +/******/ script.charset = 'utf-8'; /******/ script.src = modules.c+chunkId+modules.a; /******/ head.appendChild(script); /******/ } @@ -53,24 +54,24 @@ require.ensure(["c"], function(require) { /******/({a:".output.js",b:"webpackJsonp",c:"", /******/0: function(module, exports, require) { -var a = require(/* a */1); -var b = require(/* b */3); +var a = require(/* a */4); +var b = require(/* b */1); require.ensure(1, function(require) { - require(/* b */3).xyz(); - var d = require(/* d */2); + require(/* b */1).xyz(); + var d = require(/* d */3); }); /******/}, /******/ /******/1: function(module, exports, require) { -// module a +// module b /******/}, /******/ -/******/3: function(module, exports, require) { +/******/4: function(module, exports, require) { -// module b +// module a /******/}, /******/ @@ -83,13 +84,13 @@ require.ensure(1, function(require) { /******/webpackJsonp(1, { /******/2: function(module, exports, require) { -// module d +// module c /******/}, /******/ -/******/4: function(module, exports, require) { +/******/3: function(module, exports, require) { -// module c +// module d /******/}, /******/ @@ -99,7 +100,7 @@ require.ensure(1, function(require) { Minimized ``` javascript -webpackJsonp(1,{2:function(a,b,c){},4:function(a,b,c){}}) +webpackJsonp(1,{3:function(a,b,c){},4:function(a,b,c){}}) ``` # Info @@ -112,20 +113,20 @@ Modules: 5 Modules including duplicates: 5 Modules pre chunk: 2.5 Modules first chunk: 3 - output.js: 2033 characters + output.js: 2114 characters 1.output.js: 200 characters output.js - 0 [...]\examples\code-splitting\example.js + 0 .\example.js main - 1 [...]\examples\code-splitting\node_modules\a.js - require (1x) from [...]\examples\code-splitting\example.js - 3 [...]\examples\code-splitting\node_modules\b.js - require (2x) from [...]\examples\code-splitting\example.js + 1 .\node_modules\b.js + require (2x) from .\example.js + 4 .\node_modules\a.js + require (1x) from .\example.js 1.output.js - 2 [...]\examples\code-splitting\node_modules\d.js - async require (1x) from [...]\examples\code-splitting\example.js - 4 [...]\examples\code-splitting\node_modules\c.js - async require (1x) from [...]\examples\code-splitting\example.js + 2 .\node_modules\c.js + async require (1x) from .\example.js + 3 .\node_modules\d.js + async require (1x) from .\example.js ``` ## Minimized (uglify-js, no zip) @@ -136,18 +137,18 @@ Modules: 5 Modules including duplicates: 5 Modules pre chunk: 2.5 Modules first chunk: 3 - output.js: 729 characters + output.js: 747 characters 1.output.js: 57 characters output.js - 0 [...]\examples\code-splitting\example.js + 0 .\example.js main - 1 [...]\examples\code-splitting\node_modules\a.js - require (1x) from [...]\examples\code-splitting\example.js - 3 [...]\examples\code-splitting\node_modules\b.js - require (2x) from [...]\examples\code-splitting\example.js + 1 .\node_modules\a.js + require (1x) from .\example.js + 2 .\node_modules\b.js + require (2x) from .\example.js 1.output.js - 2 [...]\examples\code-splitting\node_modules\c.js - async require (1x) from [...]\examples\code-splitting\example.js - 4 [...]\examples\code-splitting\node_modules\d.js - async require (1x) from [...]\examples\code-splitting\example.js + 3 .\node_modules\d.js + async require (1x) from .\example.js + 4 .\node_modules\c.js + async require (1x) from .\example.js ``` diff --git a/examples/code-splitting/build.js b/examples/code-splitting/build.js index 4fe5c901c..41c29c9d1 100644 --- a/examples/code-splitting/build.js +++ b/examples/code-splitting/build.js @@ -1,18 +1 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -var cp = require('child_process'); - -var argv = process.argv; -argv.shift(); -argv.shift(); -var extraArgs = argv.join(" "); - -cp.exec("node ../../bin/webpack.js "+extraArgs+" example.js js/output.js", function (error, stdout, stderr) { - console.log('stdout:\n' + stdout); - console.log('stderr:\n ' + stderr); - if (error !== null) { - console.log('error: ' + error); - } -}); +require("../build-common"); \ No newline at end of file diff --git a/examples/code-splitting/template.md b/examples/code-splitting/template.md new file mode 100644 index 000000000..de34cba11 --- /dev/null +++ b/examples/code-splitting/template.md @@ -0,0 +1,38 @@ +# example.js + +``` javascript +{{example.js}} +``` + + +# js/output.js + +``` javascript +{{js/output.js}} +``` + +# 1.output.js + +``` javascript +{{js/1.output.js}} +``` + +Minimized + +``` javascript +{{min:js/1.output.js}} +``` + +# Info + +## Uncompressed + +``` +{{stdout}} +``` + +## Minimized (uglify-js, no zip) + +``` +{{min:stdout}} +``` diff --git a/examples/coffee-script/README.md b/examples/coffee-script/README.md index ee614fc85..fdf47fc7d 100644 --- a/examples/coffee-script/README.md +++ b/examples/coffee-script/README.md @@ -1,3 +1,4 @@ + # example.js ``` javascript @@ -45,27 +46,28 @@ module.exports = 42 /******/({ /******/0: function(module, exports, require) { -require(/* __webpack_console */1).log(require(/* ./cup1.coffee */2)); +/******/ /* WEBPACK FREE VAR INJECTION */ (function(console) { +console.log(require(/* ./cup1.coffee */2)); +/******/ /* WEBPACK FREE VAR INJECTION */ }(require(/* __webpack_console */1))) /******/}, /******/ /******/1: function(module, exports, require) { var console = window.console; -exports.log = (console && console.log) || function() {}; -exports.info = (console && console.info) || function() {}; -exports.error = (console && console.error) || function() {}; -exports.warn = (console && console.warn) || function() {}; -exports.dir = (console && console.dir) || function() {}; -exports.time = (console && console.time) || function(label) { +module.exports = console; +for(var name in {log:1, info:1, error:1, warn:1, dir:1, trace:1, assert:1}) + if(!console[name]) + console[name] = function() {}; +if(!console.time) +console.time = function(label) { times[label] = Date.now(); }; -exports.timeEnd = (console && console.timeEnd) || function() { +if(!console.timeEnd) +console.timeEnd = function() { var duration = Date.now() - times[label]; - exports.log('%s: %dms', label, duration); + console.log('%s: %dms', label, duration); }; -exports.trace = (console && console.trace) || function() {}; -exports.assert = (console && console.assert) || function() {}; /******/}, /******/ @@ -87,14 +89,16 @@ exports.assert = (console && console.assert) || function() {}; /******/ /******/3: function(module, exports, require) { +/******/ /* WEBPACK FREE VAR INJECTION */ (function(console) { (function() { - require(/* __webpack_console */1).log("yeah coffee-script"); + console.log("yeah coffee-script"); module.exports = 42; }).call(this); +/******/ /* WEBPACK FREE VAR INJECTION */ }(require(/* __webpack_console */1))) /******/}, /******/ @@ -111,17 +115,17 @@ Modules: 4 Modules including duplicates: 4 Modules pre chunk: 4 Modules first chunk: 4 - output.js: 2002 characters + output.js: 1968 characters output.js - 0 [...]\examples\coffee-script\example.js + 0 .\example.js main - 1 [...]\buildin\__webpack_console.js - require (1x) from [...]\examples\coffee-script\example.js - require (1x) from [...]\examples\coffee-script\cup2.coffee - 2 [...]\node_modules\coffee-loader.js![...]\examples\coffee-script\cup1.coffee - require (1x) from [...]\examples\coffee-script\example.js - 3 [...]\node_modules\coffee-loader.js![...]\examples\coffee-script\cup2.coffee - require (2x) from [...]\examples\coffee-script\cup1.coffee + 1 (webpack)\buildin\__webpack_console.js + require (1x) from .\example.js + require (1x) from .\cup2.coffee + 2 (webpack)\node_modules\coffee-loader\index.js!.\cup1.coffee + require (1x) from .\example.js + 3 (webpack)\node_modules\coffee-loader\index.js!.\cup2.coffee + require (2x) from .\cup1.coffee ``` ## Minimized (uglify-js, no zip) @@ -132,15 +136,15 @@ Modules: 4 Modules including duplicates: 4 Modules pre chunk: 4 Modules first chunk: 4 - output.js: 868 characters + output.js: 784 characters output.js - 0 [...]\examples\coffee-script\example.js + 0 .\example.js main - 1 [...]\buildin\__webpack_console.js - require (1x) from [...]\examples\coffee-script\example.js - require (1x) from [...]\examples\coffee-script\cup2.coffee - 2 [...]\node_modules\coffee-loader.js![...]\examples\coffee-script\cup1.coffee - require (1x) from [...]\examples\coffee-script\example.js - 3 [...]\node_modules\coffee-loader.js![...]\examples\coffee-script\cup2.coffee - require (2x) from [...]\examples\coffee-script\cup1.coffee + 1 (webpack)\buildin\__webpack_console.js + require (1x) from .\example.js + require (1x) from .\cup2.coffee + 2 (webpack)\node_modules\coffee-loader\index.js!.\cup1.coffee + require (1x) from .\example.js + 3 (webpack)\node_modules\coffee-loader\index.js!.\cup2.coffee + require (2x) from .\cup1.coffee ``` \ No newline at end of file diff --git a/examples/coffee-script/build.js b/examples/coffee-script/build.js index 4fe5c901c..41c29c9d1 100644 --- a/examples/coffee-script/build.js +++ b/examples/coffee-script/build.js @@ -1,18 +1 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -var cp = require('child_process'); - -var argv = process.argv; -argv.shift(); -argv.shift(); -var extraArgs = argv.join(" "); - -cp.exec("node ../../bin/webpack.js "+extraArgs+" example.js js/output.js", function (error, stdout, stderr) { - console.log('stdout:\n' + stdout); - console.log('stderr:\n ' + stderr); - if (error !== null) { - console.log('error: ' + error); - } -}); +require("../build-common"); \ No newline at end of file diff --git a/examples/coffee-script/template.md b/examples/coffee-script/template.md new file mode 100644 index 000000000..2f398ea95 --- /dev/null +++ b/examples/coffee-script/template.md @@ -0,0 +1,38 @@ + +# example.js + +``` javascript +{{example.js}} +``` + +# cup1.coffee + +``` coffee-script +{{cup1.coffee}} +``` + +# cup2.coffee + +``` coffee-script +{{cup2.coffee}} +``` + +# js/output.js + +``` javascript +{{js/output.js}} +``` + +# Info + +## Uncompressed + +``` +{{stdout}} +``` + +## Minimized (uglify-js, no zip) + +``` +{{min:stdout}} +``` \ No newline at end of file diff --git a/examples/loader/README.md b/examples/loader/README.md index 2ee554358..222327011 100644 --- a/examples/loader/README.md +++ b/examples/loader/README.md @@ -22,14 +22,14 @@ exports.foo = "bar"; ``` javascript module.exports = function(contents, options, callback) { - + if(contents.length !== 1) throw new Error("loader takes exactly one file as parameter"); - + if(callback) { // compile for web - callback(null /* no error */, - "exports.answer = 42;\n" + + callback(null /* no error */, + "exports.answer = 42;\n" + contents[0]); } else { // execute for node.js @@ -74,55 +74,57 @@ module.exports = function(contents, options, callback) { /******/({ /******/0: function(module, exports, require) { +/******/ /* WEBPACK FREE VAR INJECTION */ (function(console) { // Polyfill require for node.js usage of loaders -require = require(/* ../../require-polyfill */3)(require.valueOf()); +require = require(/* ../../require-polyfill */1)(require.valueOf()); // use our loader -require(/* __webpack_console */1).dir(require(/* ./loader!./file */2)); +console.dir(require(/* ./loader!./file */3)); // use buildin json loader -require(/* __webpack_console */1).dir(require(/* ./test.json */4)); // default by extension -require(/* __webpack_console */1).dir(require(/* json!./test.json */4)); // manual +console.dir(require(/* ./test.json */4)); // default by extension +console.dir(require(/* json!./test.json */4)); // manual +/******/ /* WEBPACK FREE VAR INJECTION */ }(require(/* __webpack_console */2))) /******/}, /******/ /******/1: function(module, exports, require) { -var console = window.console; -exports.log = (console && console.log) || function() {}; -exports.info = (console && console.info) || function() {}; -exports.error = (console && console.error) || function() {}; -exports.warn = (console && console.warn) || function() {}; -exports.dir = (console && console.dir) || function() {}; -exports.time = (console && console.time) || function(label) { - times[label] = Date.now(); -}; -exports.timeEnd = (console && console.timeEnd) || function() { - var duration = Date.now() - times[label]; - exports.log('%s: %dms', label, duration); -}; -exports.trace = (console && console.trace) || function() {}; -exports.assert = (console && console.assert) || function() {}; - -/******/}, -/******/ -/******/2: function(module, exports, require) { - -exports.answer = 42; -exports.foo = "bar"; - -/******/}, -/******/ -/******/3: function(module, exports, require) { - // No polyfill needed when compiled with webpack module.exports = function(r){return r} +/******/}, +/******/ +/******/2: function(module, exports, require) { + +var console = window.console; +module.exports = console; +for(var name in {log:1, info:1, error:1, warn:1, dir:1, trace:1, assert:1}) + if(!console[name]) + console[name] = function() {}; +if(!console.time) +console.time = function(label) { + times[label] = Date.now(); +}; +if(!console.timeEnd) +console.timeEnd = function() { + var duration = Date.now() - times[label]; + console.log('%s: %dms', label, duration); +}; + +/******/}, +/******/ +/******/3: function(module, exports, require) { + + + /******/}, /******/ /******/4: function(module, exports, require) { -module.exports = {"foobar":1234} +module.exports = { + "foobar": 1234 +} /******/}, /******/ @@ -149,17 +151,17 @@ Modules: 5 Modules including duplicates: 5 Modules pre chunk: 5 Modules first chunk: 5 - output.js: 2279 characters + output.js: 2048 characters output.js - 0 [...]\examples\loader\example.js + 0 .\example.js main - 1 [...]\buildin\__webpack_console.js - require (3x) from [...]\examples\loader\example.js - 2 [...]\examples\loader\loader.js![...]\examples\loader\file.js - require (1x) from [...]\examples\loader\example.js - 3 [...]\require-polyfill.web.js - require (1x) from [...]\examples\loader\example.js - 4 [...]\node_modules\json-loader.js![...]\examples\loader\test.json - require (1x) from [...]\examples\loader\example.js - require (1x) from [...]\examples\loader\example.js + 1 (webpack)\require-polyfill.web.js + require (1x) from .\example.js + 2 (webpack)\buildin\__webpack_console.js + require (3x) from .\example.js + 3 .\loader.js!.\file.js + require (1x) from .\example.js + 4 (webpack)\node_modules\json-loader\index.js!.\test.json + require (1x) from .\example.js + require (1x) from .\example.js ``` \ No newline at end of file diff --git a/examples/loader/build.js b/examples/loader/build.js index 4fe5c901c..41c29c9d1 100644 --- a/examples/loader/build.js +++ b/examples/loader/build.js @@ -1,18 +1 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -var cp = require('child_process'); - -var argv = process.argv; -argv.shift(); -argv.shift(); -var extraArgs = argv.join(" "); - -cp.exec("node ../../bin/webpack.js "+extraArgs+" example.js js/output.js", function (error, stdout, stderr) { - console.log('stdout:\n' + stdout); - console.log('stderr:\n ' + stderr); - if (error !== null) { - console.log('error: ' + error); - } -}); +require("../build-common"); \ No newline at end of file diff --git a/examples/loader/template.md b/examples/loader/template.md new file mode 100644 index 000000000..ae6b62672 --- /dev/null +++ b/examples/loader/template.md @@ -0,0 +1,47 @@ +# example.js + +``` javascript +{{example.js}} +``` + +# file.js + +``` javascript +{{file.js}} +``` + +# loader.js + +``` javascript +{{loader.js}} +``` + +# test.json + +``` javascript +{{test.json}} +``` + +# js/output.js + +``` javascript +{{js/output.js}} +``` + +# Console output + +Prints in node.js (`node example.js`) and in browser: + +``` +{ answer: 42, foo: 'bar' } +{ foobar: 1234 } +{ foobar: 1234 } +``` + +# Info + +## Uncompressed + +``` +{{stdout}} +``` \ No newline at end of file diff --git a/examples/require.context/README.md b/examples/require.context/README.md index aa12cfe79..edd6a2adf 100644 --- a/examples/require.context/README.md +++ b/examples/require.context/README.md @@ -45,40 +45,41 @@ module.exports = function() { /******/({ /******/0: function(module, exports, require) { +/******/ /* WEBPACK FREE VAR INJECTION */ (function(console) { function getTemplate(templateName) { - return require(/* ./templates */2)("./"+templateName); + return require(/* ./templates */1)("./"+templateName); } -require(/* __webpack_console */1).log(getTemplate("a")); -require(/* __webpack_console */1).log(getTemplate("b")); +console.log(getTemplate("a")); +console.log(getTemplate("b")); +/******/ /* WEBPACK FREE VAR INJECTION */ }(require(/* __webpack_console */2))) /******/}, /******/ /******/1: function(module, exports, require) { -var console = window.console; -exports.log = (console && console.log) || function() {}; -exports.info = (console && console.info) || function() {}; -exports.error = (console && console.error) || function() {}; -exports.warn = (console && console.warn) || function() {}; -exports.dir = (console && console.dir) || function() {}; -exports.time = (console && console.time) || function(label) { - times[label] = Date.now(); -}; -exports.timeEnd = (console && console.timeEnd) || function() { - var duration = Date.now() - times[label]; - exports.log('%s: %dms', label, duration); -}; -exports.trace = (console && console.trace) || function() {}; -exports.assert = (console && console.assert) || function() {}; +/***/module.exports = function(name) { +/***/ var map = {"./a.js":3,"./b.js":4,"./c.js":5}; +/***/ return require(map[name]||map[name+""]||map[name+".webpack.js"]||map[name+".web.js"]||map[name+".js"]||name); +/***/}; /******/}, /******/ /******/2: function(module, exports, require) { -/***/module.exports = function(name) { -/***/ var map = {"./a.js":3,"./b.js":5,"./c.js":4}; -/***/ return require(map[name]||map[name+".web.js"]||map[name+".js"]||name); -/***/}; +var console = window.console; +module.exports = console; +for(var name in {log:1, info:1, error:1, warn:1, dir:1, trace:1, assert:1}) + if(!console[name]) + console[name] = function() {}; +if(!console.time) +console.time = function(label) { + times[label] = Date.now(); +}; +if(!console.timeEnd) +console.timeEnd = function() { + var duration = Date.now() - times[label]; + console.log('%s: %dms', label, duration); +}; /******/}, /******/ @@ -93,7 +94,7 @@ module.exports = function() { /******/4: function(module, exports, require) { module.exports = function() { - return "This text was generated by template C"; + return "This text was generated by template B"; } /******/}, @@ -101,7 +102,7 @@ module.exports = function() { /******/5: function(module, exports, require) { module.exports = function() { - return "This text was generated by template B"; + return "This text was generated by template C"; } /******/}, @@ -119,20 +120,20 @@ Modules: 6 Modules including duplicates: 6 Modules pre chunk: 6 Modules first chunk: 6 - output.js: 2402 characters + output.js: 2274 characters output.js - 0 [...]\examples\require.context\example.js + 0 .\example.js main - 1 [...]\buildin\__webpack_console.js - require (2x) from [...]\examples\require.context\example.js - 2 generated [...]\examples\require.context\templates - context from [...]\examples\require.context\example.js - 3 [...]\examples\require.context\templates\a.js - context from [...]\examples\require.context\example.js - 4 [...]\examples\require.context\templates\c.js - context from [...]\examples\require.context\example.js - 5 [...]\examples\require.context\templates\b.js - context from [...]\examples\require.context\example.js + 1 [context] .\templates + context from .\example.js + 2 (webpack)\buildin\__webpack_console.js + require (2x) from .\example.js + 3 .\templates\a.js + context from .\example.js + 4 .\templates\b.js + context from .\example.js + 5 .\templates\c.js + context from .\example.js ``` ## Minimized (uglify-js, no zip) @@ -143,20 +144,20 @@ Modules: 6 Modules including duplicates: 6 Modules pre chunk: 6 Modules first chunk: 6 - output.js: 1119 characters + output.js: 1043 characters output.js - 0 [...]\examples\require.context\example.js + 0 .\example.js main - 1 generated [...]\examples\require.context\templates - context from [...]\examples\require.context\example.js - 2 [...]\examples\require.context\templates\a.js - context from [...]\examples\require.context\example.js - 3 [...]\examples\require.context\templates\b.js - context from [...]\examples\require.context\example.js - 4 [...]\examples\require.context\templates\c.js - context from [...]\examples\require.context\example.js - 5 [...]\buildin\__webpack_console.js - require (2x) from [...]\examples\require.context\example.js + 1 [context] .\templates + context from .\example.js + 2 (webpack)\buildin\__webpack_console.js + require (2x) from .\example.js + 3 .\templates\a.js + context from .\example.js + 4 .\templates\b.js + context from .\example.js + 5 .\templates\c.js + context from .\example.js ``` # Code Splitting diff --git a/examples/require.context/build.js b/examples/require.context/build.js index 4fe5c901c..41c29c9d1 100644 --- a/examples/require.context/build.js +++ b/examples/require.context/build.js @@ -1,18 +1 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -var cp = require('child_process'); - -var argv = process.argv; -argv.shift(); -argv.shift(); -var extraArgs = argv.join(" "); - -cp.exec("node ../../bin/webpack.js "+extraArgs+" example.js js/output.js", function (error, stdout, stderr) { - console.log('stdout:\n' + stdout); - console.log('stderr:\n ' + stderr); - if (error !== null) { - console.log('error: ' + error); - } -}); +require("../build-common"); \ No newline at end of file diff --git a/examples/require.context/template.md b/examples/require.context/template.md new file mode 100644 index 000000000..51620e0ee --- /dev/null +++ b/examples/require.context/template.md @@ -0,0 +1,43 @@ +# example.js + +``` javascript +{{example.js}} +``` + +# templates/ + +* a.js +* b.js +* c.js + +All templates are of this pattern: + +``` javascript +module.exports = function() { + return "This text was generated by template X"; +} +``` + +# js/output.js + +``` javascript +{{js/output.js}} +``` + +# Info + +## Uncompressed + +``` +{{stdout}} +``` + +## Minimized (uglify-js, no zip) + +``` +{{min:stdout}} +``` + +# Code Splitting + +See [this example combined with code splitting](/sokra/modules-webpack/tree/master/examples/code-splitted-require.context) diff --git a/examples/template-common.js b/examples/template-common.js new file mode 100644 index 000000000..1eb92489d --- /dev/null +++ b/examples/template-common.js @@ -0,0 +1,16 @@ +require = require("../require-polyfill")(require.valueOf()); + +var fs = require("fs"); + +module.exports = function(template, filesReq, stdout, prefix) { + + var regexp = new RegExp("\\{\\{" + (prefix ? prefix+":" : "") + "([^:\\}]+)\\}\\}", "g") + + return template.replace(regexp, function(match) { + match = match.substr(2 + (prefix ? prefix.length+1 : 0), match.length - 4 - (prefix ? prefix.length+1 : 0)); + if(match === "stdout") + return stdout; + return filesReq("./" + match); + }); + +} \ No newline at end of file diff --git a/lib/parse.js b/lib/parse.js index 39f7758b9..9ace09d83 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -78,7 +78,7 @@ function walkStatement(context, statement) { // Declarations case "FunctionDeclaration": - if(statement.name in context.options.overwrites) { + if(context.options.overwrites.hasOwnProperty(statement.name)) { context.overwrite.push(statement.name); } var old = addOverwrites(context, statement.params); @@ -116,7 +116,7 @@ function walkVariableDeclarators(context, declarators) { switch(declarator.type) { case "VariableDeclarator": if(declarator.id.type === "Identifier" && - declarator.id.name in context.options.overwrites) { + context.options.overwrites.hasOwnProperty(declarator.id.name)) { context.overwrite.push(declarator.id.name); } if(declarator.init) @@ -251,9 +251,8 @@ function walkExpression(context, expression) { expression.callee.object.type === "Identifier" && expression.callee.object.name === "require" && expression.callee.property.type === "Identifier" && - expression.callee.property.name in {async:1, ensure:1}) { + {async:1, ensure:1}.hasOwnProperty(expression.callee.property.name)) { var param = parseStringArray(expression.arguments[0]); - context.asyncs = context.asyncs || []; var newContext = { requires: [], namesRange: expression.arguments[0].range, @@ -266,6 +265,16 @@ function walkExpression(context, expression) { param.forEach(function(r) { newContext.requires.push({name: r}); }); + if(expression.arguments.length >= 2 && + expression.arguments[1].type === "FunctionExpression" && + expression.arguments[1].body && + expression.arguments[1].body.type === "BlockStatement" && + expression.arguments[1].body.range) + newContext.blockRange = [ + expression.arguments[1].body.range[0]+1, + expression.arguments[1].body.range[1]-1 + ]; + context.asyncs = context.asyncs || []; context.asyncs.push(newContext); context = newContext; noCallee = true; @@ -277,7 +286,7 @@ function walkExpression(context, expression) { expression.callee.object.type === "Identifier" && expression.callee.object.name === "require" && expression.callee.property.type === "Identifier" && - expression.callee.property.name in {context:1}) { + expression.callee.property.name === "context") { var param = parseString(expression.arguments[0]); context.contexts = context.contexts || []; var newContext = { @@ -295,7 +304,7 @@ function walkExpression(context, expression) { expression.callee.object.type === "Identifier" && expression.callee.object.name === "require" && expression.callee.property.type === "Identifier" && - expression.callee.property.name in {valueOf:1}) { + expression.callee.property.name === "valueOf") { noCallee = true; } @@ -328,13 +337,20 @@ function walkExpression(context, expression) { }; context.contexts.push(newContext); } else if(context.overwrite.indexOf(expression.name) === -1 && - expression.name in context.options.overwrites) { + context.options.overwrites.hasOwnProperty(expression.name)) { context.requires = context.requires || []; + var overwrite = context.options.overwrites[expression.name]; + var append = undefined; + if(overwrite.indexOf("+") !== -1) { + append = overwrite.substr(overwrite.indexOf("+")+1); + overwrite = overwrite.substr(0, overwrite.indexOf("+")); + } context.requires.push({ - name: context.options.overwrites[expression.name], - expressionRange: expression.range, + name: overwrite, line: expression.loc.start.line, - column: expression.loc.start.column + column: expression.loc.start.column, + variable: expression.name, + append: append }); } break; @@ -350,7 +366,7 @@ function addOverwrites(context, params) { return; } if(param.type === "Identifier" && - param.name in context.options.overwrites) + context.options.overwrites.hasOwnProperty(param.name)) context.overwrite.push(param.name); }); return l; @@ -430,5 +446,6 @@ module.exports = function parse(source, options) { overwrite: [] }; walkStatements(context, ast.body); + delete context.options; return context; } \ No newline at end of file diff --git a/lib/templateAsync.js b/lib/templateAsync.js index ffa756b6a..87ad0b228 100644 --- a/lib/templateAsync.js +++ b/lib/templateAsync.js @@ -20,6 +20,7 @@ /******/ var head = document.getElementsByTagName('head')[0]; /******/ var script = document.createElement('script'); /******/ script.type = 'text/javascript'; +/******/ script.charset = 'utf-8'; /******/ script.src = modules.c+chunkId+modules.a; /******/ head.appendChild(script); /******/ } diff --git a/lib/webpack.js b/lib/webpack.js index 61785986d..dbf1c5760 100644 --- a/lib/webpack.js +++ b/lib/webpack.js @@ -70,9 +70,12 @@ module.exports = function(context, moduleName, options, callback) { options.parse = options.parse || {}; options.parse.overwrites = options.parse.overwrites || {}; options.parse.overwrites.process = options.parse.overwrites.process || ("__webpack_process"); - options.parse.overwrites.module = options.parse.overwrites.module || ("__webpack_module"); + options.parse.overwrites.module = options.parse.overwrites.module || ("__webpack_module+(module)"); options.parse.overwrites.console = options.parse.overwrites.console || ("__webpack_console"); options.parse.overwrites.global = options.parse.overwrites.global || ("__webpack_global"); + options.parse.overwrites.Buffer = options.parse.overwrites.Buffer || ("buffer+.Buffer"); + options.parse.overwrites["__dirname"] = options.parse.overwrites["__dirname"] || ("__webpack_dirname"); + options.parse.overwrites["__filename"] = options.parse.overwrites["__filename"] || ("__webpack_filename"); options.resolve = options.resolve || {}; options.resolve.paths = options.resolve.paths || []; options.resolve.paths.push(path.join(path.dirname(__dirname), "buildin")); diff --git a/lib/writeSource.js b/lib/writeSource.js index c83b7b9ed..e53f46172 100644 --- a/lib/writeSource.js +++ b/lib/writeSource.js @@ -20,7 +20,10 @@ module.exports = function(module, options) { } return; } + var freeVars = {}; var replaces = []; // { from: 123, to: 125, value: "4" } + var modulePrepends = []; + var moduleAppends = []; function genReplaceRequire(requireItem) { if(requireItem.id !== undefined) { var prefix = ""; @@ -30,7 +33,7 @@ module.exports = function(module, options) { replaces.push({ from: requireItem.expressionRange[0], to: requireItem.expressionRange[1], - value: "require(" + prefix + requireItem.id + ")" + value: "require(" + prefix + requireItem.id + ")" + (requireItem.append || "") }); } else if(requireItem.valueRange) { replaces.push({ @@ -38,6 +41,10 @@ module.exports = function(module, options) { to: requireItem.valueRange[1], value: prefix + requireItem.id }); + } else if(requireItem.variable) { + if(!freeVars[requireItem.variable]) { + freeVars[requireItem.variable] = requireItem; + } } } } @@ -74,6 +81,8 @@ module.exports = function(module, options) { } if(module.asyncs) { module.asyncs.forEach(function genReplacesAsync(asyncItem) { + var oldFreeVars = freeVars; + freeVars = {}; if(asyncItem.requires) { asyncItem.requires.forEach(genReplaceRequire); } @@ -90,8 +99,54 @@ module.exports = function(module, options) { value: ((asyncItem.chunkId || "0") + "") }); } + if(asyncItem.blockRange) { + genReplacesFreeVars(asyncItem.blockRange, freeVars); + } + freeVars = oldFreeVars; }); } + function genReplacesFreeVars(blockRange, freeVars) { + var keys = Object.keys(freeVars); + var values = []; + var removeKeys = []; + keys.forEach(function(key, idx) { + if(freeVars[key].id === module.id) { + removeKeys.push(idx); + } else { + values.push(freeVars[key]); + } + }); + removeKeys.reverse().forEach(function(idx) { + keys.splice(idx, 1); + }); + if(keys.length === 0) return; + values.forEach(function(requireItem, idx) { + if(requireItem.id !== undefined) { + var prefix = ""; + if(requireItem.name) + prefix += "/* " + requireItem.name + " */"; + values[idx] = "require(" + prefix + requireItem.id + ")" + (requireItem.append || ""); + } + }); + var start = "/* WEBPACK FREE VAR INJECTION */ (function(" + keys.join(",") + ") {"; + var end = "/* WEBPACK FREE VAR INJECTION */ }(" + values.join(",") + "))" + if(blockRange) { + replaces.push({ + from: blockRange[0], + to: blockRange[0]-1, + value: start + }); + replaces.push({ + from: blockRange[1], + to: blockRange[1]-1, + value: end + }); + } else { + modulePrepends.unshift("/******/ " + start + "\n"); + moduleAppends.push("\n/******/ " + end); + } + } + genReplacesFreeVars(null, freeVars); replaces.sort(function(a, b) { return b.from - a.from; }); @@ -111,9 +166,12 @@ module.exports = function(module, options) { } result.push("\n\n// WEBPACK FOOTER //\n"+ "// module.id = " + module.id + "\n" + + "// module.chunks = " + module.chunks.join(", ") + "\n" + "//@ sourceURL=webpack-module://" + encodeURI(module.filename).replace(/%5C|%2F/g, "/")); - return "eval(" + JSON.stringify(result.join("")) + ")"; + result = ["eval(", JSON.stringify(result.join("")), ")"]; } + result.unshift.apply(result, modulePrepends.reverse()); + result.push.apply(result, moduleAppends); return result.join(""); } diff --git a/package.json b/package.json index 7c1fbb1ac..927bb43a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webpack", - "version": "0.3.0", + "version": "0.3.1", "author": "Tobias Koppers @sokra", "description": "Packs CommonJs Modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand.", "dependencies": { diff --git a/test/browsertest/build.js b/test/browsertest/build.js index af91ac7cc..488aa564b 100644 --- a/test/browsertest/build.js +++ b/test/browsertest/build.js @@ -9,24 +9,38 @@ argv.shift(); argv.shift(); var extraArgs = argv.join(" "); -cp.exec("node ../../bin/webpack.js "+extraArgs+" --colors --single --libary libary1 node_modules/libary1 js/libary1.js", function (error, stdout, stderr) { - console.log('libary1 stdout:\n' + stdout); - console.log('libary1 stderr:\n ' + stderr); - if (error !== null) { - console.log('libary1 error: ' + error); - } -}); -cp.exec("node ../../bin/webpack.js "+extraArgs+" --colors --script-src-prefix js/ --libary libary2 node_modules/libary2 js/libary2.js", function (error, stdout, stderr) { - console.log('libary2 stdout:\n' + stdout); - console.log('libary2 stderr:\n ' + stderr); - if (error !== null) { - console.log('libary2 error: ' + error); - } -}); -cp.exec("node ../../bin/webpack.js "+extraArgs+" --colors --script-src-prefix js/ lib/index js/web.js", function (error, stdout, stderr) { - console.log('web stdout:\n' + stdout); - console.log('web stderr:\n ' + stderr); - if (error !== null) { - console.log('web error: ' + error); - } -}); \ No newline at end of file +try { + require("vm-browserify"); + compile(); +} catch(e) { + console.log("install vm-browserify..."); + cp.exec("npm install vm-browserify", function (error, stdout, stderr) { + console.log(stdout); + compile(); + }); +} +function compile() { + console.log("compile scripts..."); + + cp.exec("node ../../bin/webpack.js "+extraArgs+" --colors --single --libary libary1 node_modules/libary1 js/libary1.js", function (error, stdout, stderr) { + console.log('libary1 stdout:\n' + stdout); + console.log('libary1 stderr:\n ' + stderr); + if (error !== null) { + console.log('libary1 error: ' + error); + } + }); + cp.exec("node ../../bin/webpack.js "+extraArgs+" --colors --script-src-prefix js/ --libary libary2 node_modules/libary2 js/libary2.js", function (error, stdout, stderr) { + console.log('libary2 stdout:\n' + stdout); + console.log('libary2 stderr:\n ' + stderr); + if (error !== null) { + console.log('libary2 error: ' + error); + } + }); + cp.exec("node ../../bin/webpack.js "+extraArgs+" --colors --alias vm=vm-browserify --script-src-prefix js/ lib/index js/web.js", function (error, stdout, stderr) { + console.log('web stdout:\n' + stdout); + console.log('web stderr:\n ' + stderr); + if (error !== null) { + console.log('web error: ' + error); + } + }); +} diff --git a/test/browsertest/lib/index.web.js b/test/browsertest/lib/index.web.js index 789145eca..9433e29da 100644 --- a/test/browsertest/lib/index.web.js +++ b/test/browsertest/lib/index.web.js @@ -127,3 +127,8 @@ var abc = "abc", scr = "script.coffee"; window.test(require("../resources/" + scr) === "coffee test", "context should process extensions"); window.test(require("raw!../resources/" + abc + ".txt") === "abc", "raw loader with context"); + +require.ensure([], function(require) { + // Tests from node.js + require("../nodetests"); +}); diff --git a/test/browsertest/nodetests/common.js b/test/browsertest/nodetests/common.js new file mode 100644 index 000000000..e0ac7a09e --- /dev/null +++ b/test/browsertest/nodetests/common.js @@ -0,0 +1,146 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var path = require('path'); +var assert = require('assert'); + +exports.testDir = path.dirname(__filename); +exports.fixturesDir = path.join(exports.testDir, 'fixtures'); +exports.libDir = path.join(exports.testDir, '../lib'); +exports.tmpDir = path.join(exports.testDir, 'tmp'); +exports.PORT = 12346; + +if (process.platform === 'win32') { + exports.PIPE = '\\\\.\\pipe\\libuv-test'; +} else { + exports.PIPE = exports.tmpDir + '/test.sock'; +} + +var util = require('util'); +for (var i in util) exports[i] = util[i]; +//for (var i in exports) global[i] = exports[i]; + +function protoCtrChain(o) { + var result = []; + for (; o; o = o.__proto__) { result.push(o.constructor); } + return result.join(); +} + +exports.indirectInstanceOf = function(obj, cls) { + if (obj instanceof cls) { return true; } + var clsChain = protoCtrChain(cls.prototype); + var objChain = protoCtrChain(obj); + return objChain.slice(-clsChain.length) === clsChain; +}; + + +exports.ddCommand = function(filename, kilobytes) { + if (process.platform === 'win32') { + var p = path.resolve(exports.fixturesDir, 'create-file.js'); + return '"' + process.argv[0] + '" "' + p + '" "' + + filename + '" ' + (kilobytes * 1024); + } else { + return 'dd if=/dev/zero of="' + filename + '" bs=1024 count=' + kilobytes; + } +}; + + +exports.spawnPwd = function(options) { + var spawn = require('child_process').spawn; + + if (process.platform === 'win32') { + return spawn('cmd.exe', ['/c', 'cd'], options); + } else { + return spawn('pwd', [], options); + } +}; + + +// Turn this off if the test should not check for global leaks. +exports.globalCheck = true; + +process.on('exit', function() { + if (!exports.globalCheck) return; + var knownGlobals = [setTimeout, + setInterval, + clearTimeout, + clearInterval, + console, + Buffer, + process, + global]; + + if (global.errno) { + knownGlobals.push(errno); + } + + if (global.gc) { + knownGlobals.push(gc); + } + + if (global.DTRACE_HTTP_SERVER_RESPONSE) { + knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE); + knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST); + knownGlobals.push(DTRACE_HTTP_CLIENT_RESPONSE); + knownGlobals.push(DTRACE_HTTP_CLIENT_REQUEST); + knownGlobals.push(DTRACE_NET_STREAM_END); + knownGlobals.push(DTRACE_NET_SERVER_CONNECTION); + knownGlobals.push(DTRACE_NET_SOCKET_READ); + knownGlobals.push(DTRACE_NET_SOCKET_WRITE); + } + + if (global.ArrayBuffer) { + knownGlobals.push(ArrayBuffer); + knownGlobals.push(Int8Array); + knownGlobals.push(Uint8Array); + knownGlobals.push(Uint8ClampedArray); + knownGlobals.push(Int16Array); + knownGlobals.push(Uint16Array); + knownGlobals.push(Int32Array); + knownGlobals.push(Uint32Array); + knownGlobals.push(Float32Array); + knownGlobals.push(Float64Array); + knownGlobals.push(DataView); + } + + for (var x in global) { + var found = false; + + for (var y in knownGlobals) { + if (global[x] === knownGlobals[y]) { + found = true; + break; + } + } + + if (!found) { + console.error('Unknown global: %s', x); + assert.ok(false, 'Unknown global found'); + } + } +}); + + +// This function allows one two run an HTTP test agaist both HTTPS and +// normal HTTP modules. This ensures they fit the same API. +exports.httpTest = function httpTest(cb) { +}; + diff --git a/test/browsertest/nodetests/fixtures/global/plain.js b/test/browsertest/nodetests/fixtures/global/plain.js new file mode 100644 index 000000000..f983d7c68 --- /dev/null +++ b/test/browsertest/nodetests/fixtures/global/plain.js @@ -0,0 +1,25 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +foo = 'foo'; +global.bar = 'bar'; + +exports.fooBar = {foo: global.foo, bar: bar}; diff --git a/test/browsertest/nodetests/index.js b/test/browsertest/nodetests/index.js new file mode 100644 index 000000000..601120cec --- /dev/null +++ b/test/browsertest/nodetests/index.js @@ -0,0 +1,26 @@ +require("./common").globalCheck = false; + +require("./simple/test-assert.js"); +require("./simple/test-event-emitter-check-listener-leaks.js"); +require("./simple/test-event-emitter-modify-in-emit.js"); +require("./simple/test-event-emitter-num-args.js"); +require("./simple/test-event-emitter-remove-all-listeners.js"); +require("./simple/test-global.js"); +require("./simple/test-next-tick-doesnt-hang.js"); +require("./simple/test-next-tick-ordering2.js"); +require("./simple/test-path.js"); +require("./simple/test-querystring.js"); +require("./simple/test-sys.js"); +require("./simple/test-timers-zero-timeout.js"); +require("./simple/test-timers.js"); +require("./simple/test-url.js"); +require("./simple/test-util-format.js"); +require("./simple/test-util-inspect.js"); +require("./simple/test-util.js"); + +window.test(true, "Node.js simple tests should complete"); + +setTimeout(function() { + process.emit("exit"); + window.test(true, "Node.js simple tests should complete on process exit"); +}, 3000); \ No newline at end of file diff --git a/test/browsertest/nodetests/simple/test-assert.js b/test/browsertest/nodetests/simple/test-assert.js new file mode 100644 index 000000000..13c7e494e --- /dev/null +++ b/test/browsertest/nodetests/simple/test-assert.js @@ -0,0 +1,285 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var a = require('assert'); + +function makeBlock(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function() { + return f.apply(this, args); + }; +} + +assert.ok(common.indirectInstanceOf(a.AssertionError.prototype, Error), + 'a.AssertionError instanceof Error'); + +assert.throws(makeBlock(a, false), a.AssertionError, 'ok(false)'); + +assert.doesNotThrow(makeBlock(a, true), a.AssertionError, 'ok(true)'); + +assert.doesNotThrow(makeBlock(a, 'test', 'ok(\'test\')')); + +assert.throws(makeBlock(a.ok, false), + a.AssertionError, 'ok(false)'); + +assert.doesNotThrow(makeBlock(a.ok, true), + a.AssertionError, 'ok(true)'); + +assert.doesNotThrow(makeBlock(a.ok, 'test'), 'ok(\'test\')'); + +assert.throws(makeBlock(a.equal, true, false), a.AssertionError, 'equal'); + +assert.doesNotThrow(makeBlock(a.equal, null, null), 'equal'); + +assert.doesNotThrow(makeBlock(a.equal, undefined, undefined), 'equal'); + +assert.doesNotThrow(makeBlock(a.equal, null, undefined), 'equal'); + +assert.doesNotThrow(makeBlock(a.equal, true, true), 'equal'); + +assert.doesNotThrow(makeBlock(a.equal, 2, '2'), 'equal'); + +assert.doesNotThrow(makeBlock(a.notEqual, true, false), 'notEqual'); + +assert.throws(makeBlock(a.notEqual, true, true), + a.AssertionError, 'notEqual'); + +assert.throws(makeBlock(a.strictEqual, 2, '2'), + a.AssertionError, 'strictEqual'); + +assert.throws(makeBlock(a.strictEqual, null, undefined), + a.AssertionError, 'strictEqual'); + +assert.doesNotThrow(makeBlock(a.notStrictEqual, 2, '2'), 'notStrictEqual'); + +// deepEquals joy! +// 7.2 +assert.doesNotThrow(makeBlock(a.deepEqual, new Date(2000, 3, 14), + new Date(2000, 3, 14)), 'deepEqual date'); + +assert.throws(makeBlock(a.deepEqual, new Date(), new Date(2000, 3, 14)), + a.AssertionError, + 'deepEqual date'); + +// 7.3 +assert.doesNotThrow(makeBlock(a.deepEqual, /a/, /a/)); +assert.doesNotThrow(makeBlock(a.deepEqual, /a/g, /a/g)); +assert.doesNotThrow(makeBlock(a.deepEqual, /a/i, /a/i)); +assert.doesNotThrow(makeBlock(a.deepEqual, /a/m, /a/m)); +assert.doesNotThrow(makeBlock(a.deepEqual, /a/igm, /a/igm)); +assert.throws(makeBlock(a.deepEqual, /ab/, /a/)); +assert.throws(makeBlock(a.deepEqual, /a/g, /a/)); +assert.throws(makeBlock(a.deepEqual, /a/i, /a/)); +assert.throws(makeBlock(a.deepEqual, /a/m, /a/)); +assert.throws(makeBlock(a.deepEqual, /a/igm, /a/im)); + +var re1 = /a/; +re1.lastIndex = 3; +assert.throws(makeBlock(a.deepEqual, re1, /a/)); + + +// 7.4 +assert.doesNotThrow(makeBlock(a.deepEqual, 4, '4'), 'deepEqual == check'); +assert.doesNotThrow(makeBlock(a.deepEqual, true, 1), 'deepEqual == check'); +assert.throws(makeBlock(a.deepEqual, 4, '5'), + a.AssertionError, + 'deepEqual == check'); + +// 7.5 +// having the same number of owned properties && the same set of keys +assert.doesNotThrow(makeBlock(a.deepEqual, {a: 4}, {a: 4})); +assert.doesNotThrow(makeBlock(a.deepEqual, {a: 4, b: '2'}, {a: 4, b: '2'})); +assert.doesNotThrow(makeBlock(a.deepEqual, [4], ['4'])); +assert.throws(makeBlock(a.deepEqual, {a: 4}, {a: 4, b: true}), + a.AssertionError); +assert.doesNotThrow(makeBlock(a.deepEqual, ['a'], {0: 'a'})); +//(although not necessarily the same order), +assert.doesNotThrow(makeBlock(a.deepEqual, {a: 4, b: '1'}, {b: '1', a: 4})); +var a1 = [1, 2, 3]; +var a2 = [1, 2, 3]; +a1.a = 'test'; +a1.b = true; +a2.b = true; +a2.a = 'test'; +assert.throws(makeBlock(a.deepEqual, Object.keys(a1), Object.keys(a2)), + a.AssertionError); +assert.doesNotThrow(makeBlock(a.deepEqual, a1, a2)); + +// having an identical prototype property +var nbRoot = { + toString: function() { return this.first + ' ' + this.last; } +}; + +function nameBuilder(first, last) { + this.first = first; + this.last = last; + return this; +} +nameBuilder.prototype = nbRoot; + +function nameBuilder2(first, last) { + this.first = first; + this.last = last; + return this; +} +nameBuilder2.prototype = nbRoot; + +var nb1 = new nameBuilder('Ryan', 'Dahl'); +var nb2 = new nameBuilder2('Ryan', 'Dahl'); + +assert.doesNotThrow(makeBlock(a.deepEqual, nb1, nb2)); + +nameBuilder2.prototype = Object; +nb2 = new nameBuilder2('Ryan', 'Dahl'); +assert.throws(makeBlock(a.deepEqual, nb1, nb2), a.AssertionError); + +// String literal + object blew up my implementation... +assert.throws(makeBlock(a.deepEqual, 'a', {}), a.AssertionError); + +// Testing the throwing +function thrower(errorConstructor) { + throw new errorConstructor('test'); +} +var aethrow = makeBlock(thrower, a.AssertionError); +aethrow = makeBlock(thrower, a.AssertionError); + +// the basic calls work +assert.throws(makeBlock(thrower, a.AssertionError), + a.AssertionError, 'message'); +assert.throws(makeBlock(thrower, a.AssertionError), a.AssertionError); +assert.throws(makeBlock(thrower, a.AssertionError)); + +// if not passing an error, catch all. +assert.throws(makeBlock(thrower, TypeError)); + +// when passing a type, only catch errors of the appropriate type +var threw = false; +try { + a.throws(makeBlock(thrower, TypeError), a.AssertionError); +} catch (e) { + threw = true; + assert.ok(e instanceof TypeError, 'type'); +} +assert.equal(true, threw, + 'a.throws with an explicit error is eating extra errors', + a.AssertionError); +threw = false; + +// doesNotThrow should pass through all errors +try { + a.doesNotThrow(makeBlock(thrower, TypeError), a.AssertionError); +} catch (e) { + threw = true; + assert.ok(e instanceof TypeError); +} +assert.equal(true, threw, + 'a.doesNotThrow with an explicit error is eating extra errors'); + +// key difference is that throwing our correct error makes an assertion error +try { + a.doesNotThrow(makeBlock(thrower, TypeError), TypeError); +} catch (e) { + threw = true; + assert.ok(e instanceof a.AssertionError); +} +assert.equal(true, threw, + 'a.doesNotThrow is not catching type matching errors'); + +assert.throws(function() {assert.ifError(new Error('test error'))}); +assert.doesNotThrow(function() {assert.ifError(null)}); +assert.doesNotThrow(function() {assert.ifError()}); + +// make sure that validating using constructor really works +threw = false; +try { + assert.throws( + function() { + throw ({}); + }, + Array + ); +} catch (e) { + threw = true; +} +assert.ok(threw, 'wrong constructor validation'); + +// use a RegExp to validate error message +a.throws(makeBlock(thrower, TypeError), /test/); + +// use a fn to validate error object +a.throws(makeBlock(thrower, TypeError), function(err) { + if ((err instanceof TypeError) && /test/.test(err)) { + return true; + } +}); + + +// GH-207. Make sure deepEqual doesn't loop forever on circular refs + +var b = {}; +b.b = b; + +var c = {}; +c.b = c; + +var gotError = false; +try { + assert.deepEqual(b, c); +} catch (e) { + gotError = true; +} + +console.log('All OK'); +assert.ok(gotError); + + +// #217 +function testAssertionMessage(actual, expected) { + try { + assert.equal(actual, ''); + } catch (e) { + assert.equal(e.toString(), + ['AssertionError:', expected, '==', '""'].join(' ')); + } +} +testAssertionMessage(undefined, '"undefined"'); +testAssertionMessage(null, 'null'); +testAssertionMessage(true, 'true'); +testAssertionMessage(false, 'false'); +testAssertionMessage(0, '0'); +testAssertionMessage(100, '100'); +testAssertionMessage(NaN, '"NaN"'); +testAssertionMessage(Infinity, '"Infinity"'); +testAssertionMessage(-Infinity, '"-Infinity"'); +testAssertionMessage('', '""'); +testAssertionMessage('foo', '"foo"'); +testAssertionMessage([], '[]'); +testAssertionMessage([1, 2, 3], '[1,2,3]'); +testAssertionMessage(/a/, '"/a/"'); +testAssertionMessage(/abc/gim, '"/abc/gim"'); +testAssertionMessage(function f() {}, '"function f() {}"'); +testAssertionMessage({}, '{}'); +testAssertionMessage({a: undefined, b: null}, '{"a":"undefined","b":null}'); +testAssertionMessage({a: NaN, b: Infinity, c: -Infinity}, + '{"a":"NaN","b":"Infinity","c":"-Infinity"}'); + diff --git a/test/browsertest/nodetests/simple/test-event-emitter-check-listener-leaks.js b/test/browsertest/nodetests/simple/test-event-emitter-check-listener-leaks.js new file mode 100644 index 000000000..7d9c20324 --- /dev/null +++ b/test/browsertest/nodetests/simple/test-event-emitter-check-listener-leaks.js @@ -0,0 +1,58 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var events = require('events'); + +var e = new events.EventEmitter(); + +// default +for (var i = 0; i < 10; i++) { + e.on('default', function() {}); +} +assert.ok(!e._events['default'].hasOwnProperty('warned')); +e.on('default', function() {}); +assert.ok(e._events['default'].warned); + +// specific +e.setMaxListeners(5); +for (var i = 0; i < 5; i++) { + e.on('specific', function() {}); +} +assert.ok(!e._events['specific'].hasOwnProperty('warned')); +e.on('specific', function() {}); +assert.ok(e._events['specific'].warned); + +// only one +e.setMaxListeners(1); +e.on('only one', function() {}); +assert.ok(!e._events['only one'].hasOwnProperty('warned')); +e.on('only one', function() {}); +assert.ok(e._events['only one'].hasOwnProperty('warned')); + +// unlimited +e.setMaxListeners(0); +for (var i = 0; i < 1000; i++) { + e.on('unlimited', function() {}); +} +assert.ok(!e._events['unlimited'].hasOwnProperty('warned')); + diff --git a/test/browsertest/nodetests/simple/test-event-emitter-modify-in-emit.js b/test/browsertest/nodetests/simple/test-event-emitter-modify-in-emit.js new file mode 100644 index 000000000..738af098c --- /dev/null +++ b/test/browsertest/nodetests/simple/test-event-emitter-modify-in-emit.js @@ -0,0 +1,77 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var events = require('events'); + +var callbacks_called = []; + +var e = new events.EventEmitter(); + +function callback1() { + callbacks_called.push('callback1'); + e.on('foo', callback2); + e.on('foo', callback3); + e.removeListener('foo', callback1); +} + +function callback2() { + callbacks_called.push('callback2'); + e.removeListener('foo', callback2); +} + +function callback3() { + callbacks_called.push('callback3'); + e.removeListener('foo', callback3); +} + +e.on('foo', callback1); +assert.equal(1, e.listeners('foo').length); + +e.emit('foo'); +assert.equal(2, e.listeners('foo').length); +assert.deepEqual(['callback1'], callbacks_called); + +e.emit('foo'); +assert.equal(0, e.listeners('foo').length); +assert.deepEqual(['callback1', 'callback2', 'callback3'], callbacks_called); + +e.emit('foo'); +assert.equal(0, e.listeners('foo').length); +assert.deepEqual(['callback1', 'callback2', 'callback3'], callbacks_called); + +e.on('foo', callback1); +e.on('foo', callback2); +assert.equal(2, e.listeners('foo').length); +e.removeAllListeners('foo'); +assert.equal(0, e.listeners('foo').length); + +// Verify that removing callbacks while in emit allows emits to propagate to +// all listeners +callbacks_called = []; + +e.on('foo', callback2); +e.on('foo', callback3); +assert.equal(2, e.listeners('foo').length); +e.emit('foo'); +assert.deepEqual(['callback2', 'callback3'], callbacks_called); +assert.equal(0, e.listeners('foo').length); diff --git a/test/browsertest/nodetests/simple/test-event-emitter-num-args.js b/test/browsertest/nodetests/simple/test-event-emitter-num-args.js new file mode 100644 index 000000000..037c56965 --- /dev/null +++ b/test/browsertest/nodetests/simple/test-event-emitter-num-args.js @@ -0,0 +1,48 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var events = require('events'); + +var e = new events.EventEmitter(), + num_args_emited = []; + +e.on('numArgs', function() { + var numArgs = arguments.length; + console.log('numArgs: ' + numArgs); + num_args_emited.push(numArgs); +}); + +console.log('start'); + +e.emit('numArgs'); +e.emit('numArgs', null); +e.emit('numArgs', null, null); +e.emit('numArgs', null, null, null); +e.emit('numArgs', null, null, null, null); +e.emit('numArgs', null, null, null, null, null); + +process.on('exit', function() { + assert.deepEqual([0, 1, 2, 3, 4, 5], num_args_emited); +}); + + diff --git a/test/browsertest/nodetests/simple/test-event-emitter-remove-all-listeners.js b/test/browsertest/nodetests/simple/test-event-emitter-remove-all-listeners.js new file mode 100644 index 000000000..6e27f3eb7 --- /dev/null +++ b/test/browsertest/nodetests/simple/test-event-emitter-remove-all-listeners.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var events = require('events'); + + +function listener() {} + +var e1 = new events.EventEmitter(); +e1.on('foo', listener); +e1.on('bar', listener); +e1.on('baz', listener); +e1.on('baz', listener); +var fooListeners = e1.listeners('foo'); +var barListeners = e1.listeners('bar'); +var bazListeners = e1.listeners('baz'); +e1.removeAllListeners('bar'); +e1.removeAllListeners('baz'); +assert.deepEqual(e1.listeners('foo'), [listener]); +assert.deepEqual(e1.listeners('bar'), []); +assert.deepEqual(e1.listeners('baz'), []); +// identity check, the array should not change +assert.equal(e1.listeners('foo'), fooListeners); +assert.equal(e1.listeners('bar'), barListeners); +assert.equal(e1.listeners('baz'), bazListeners); + +var e2 = new events.EventEmitter(); +e2.on('foo', listener); +e2.on('bar', listener); +e2.removeAllListeners(); +console.error(e2); +assert.deepEqual([], e2.listeners('foo')); +assert.deepEqual([], e2.listeners('bar')); diff --git a/test/browsertest/nodetests/simple/test-global.js b/test/browsertest/nodetests/simple/test-global.js new file mode 100644 index 000000000..4cc9799ba --- /dev/null +++ b/test/browsertest/nodetests/simple/test-global.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +common.globalCheck = false; + +baseFoo = 'foo'; +global.baseBar = 'bar'; + +assert.equal('foo', global.baseFoo, 'x -> global.x in base level not working'); + +assert.equal('bar', baseBar, 'global.x -> x in base level not working'); + +var module = require('../fixtures/global/plain'), + fooBar = module.fooBar; + +assert.equal('foo', fooBar.foo, 'x -> global.x in sub level not working'); + +assert.equal('bar', fooBar.bar, 'global.x -> x in sub level not working'); diff --git a/test/browsertest/nodetests/simple/test-next-tick-doesnt-hang.js b/test/browsertest/nodetests/simple/test-next-tick-doesnt-hang.js new file mode 100644 index 000000000..3d26bd3c6 --- /dev/null +++ b/test/browsertest/nodetests/simple/test-next-tick-doesnt-hang.js @@ -0,0 +1,30 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +/* + * This test verifies that having a single nextTick statement and nothing else + * does not hang the event loop. If this test times out it has failed. + */ + +process.nextTick(function() { + // Nothing +}); diff --git a/test/browsertest/nodetests/simple/test-next-tick-ordering2.js b/test/browsertest/nodetests/simple/test-next-tick-ordering2.js new file mode 100644 index 000000000..94fd20fbb --- /dev/null +++ b/test/browsertest/nodetests/simple/test-next-tick-ordering2.js @@ -0,0 +1,38 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var order = []; +process.nextTick(function() { + setTimeout(function() { + order.push('setTimeout'); + }, 0); + + process.nextTick(function() { + order.push('nextTick'); + }); +}); + +process.on('exit', function() { + assert.deepEqual(order, ['nextTick', 'setTimeout']); +}); diff --git a/test/browsertest/nodetests/simple/test-path.js b/test/browsertest/nodetests/simple/test-path.js new file mode 100644 index 000000000..02386c8bc --- /dev/null +++ b/test/browsertest/nodetests/simple/test-path.js @@ -0,0 +1,275 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var path = require('path'); + +var isWindows = process.platform === 'win32'; + +var f = "nodetest/simple/test-path.js"; + +assert.equal(path.basename(f), 'test-path.js'); +assert.equal(path.basename(f, '.js'), 'test-path'); + +// POSIX filenames may include control characters +// c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html +if (!isWindows) { + var controlCharFilename = 'Icon' + String.fromCharCode(13); + assert.equal(path.basename('/a/b/' + controlCharFilename), + controlCharFilename); +} + +assert.equal(path.extname(f), '.js'); + +assert.equal(path.dirname(f).substr(-11), + isWindows ? 'test\\simple' : 'test/simple'); +assert.equal(path.dirname('/a/b/'), '/a'); +assert.equal(path.dirname('/a/b'), '/a'); +assert.equal(path.dirname('/a'), '/'); +assert.equal(path.dirname('/'), '/'); + +if (isWindows) { + assert.equal(path.dirname('c:\\'), 'c:\\'); + assert.equal(path.dirname('c:\\foo'), 'c:\\'); + assert.equal(path.dirname('c:\\foo\\'), 'c:\\'); + assert.equal(path.dirname('c:\\foo\\bar'), 'c:\\foo'); + assert.equal(path.dirname('c:\\foo\\bar\\'), 'c:\\foo'); + assert.equal(path.dirname('c:\\foo\\bar\\baz'), 'c:\\foo\\bar'); + assert.equal(path.dirname('\\'), '\\'); + assert.equal(path.dirname('\\foo'), '\\'); + assert.equal(path.dirname('\\foo\\'), '\\'); + assert.equal(path.dirname('\\foo\\bar'), '\\foo'); + assert.equal(path.dirname('\\foo\\bar\\'), '\\foo'); + assert.equal(path.dirname('\\foo\\bar\\baz'), '\\foo\\bar'); + assert.equal(path.dirname('c:'), 'c:'); + assert.equal(path.dirname('c:foo'), 'c:'); + assert.equal(path.dirname('c:foo\\'), 'c:'); + assert.equal(path.dirname('c:foo\\bar'), 'c:foo'); + assert.equal(path.dirname('c:foo\\bar\\'), 'c:foo'); + assert.equal(path.dirname('c:foo\\bar\\baz'), 'c:foo\\bar'); + assert.equal(path.dirname('\\\\unc\\share'), '\\\\unc\\share'); + assert.equal(path.dirname('\\\\unc\\share\\foo'), '\\\\unc\\share\\'); + assert.equal(path.dirname('\\\\unc\\share\\foo\\'), '\\\\unc\\share\\'); + assert.equal(path.dirname('\\\\unc\\share\\foo\\bar'), + '\\\\unc\\share\\foo'); + assert.equal(path.dirname('\\\\unc\\share\\foo\\bar\\'), + '\\\\unc\\share\\foo'); + assert.equal(path.dirname('\\\\unc\\share\\foo\\bar\\baz'), + '\\\\unc\\share\\foo\\bar'); +} + + +assert.equal(path.extname(''), ''); +assert.equal(path.extname('/path/to/file'), ''); +assert.equal(path.extname('/path/to/file.ext'), '.ext'); +assert.equal(path.extname('/path.to/file.ext'), '.ext'); +assert.equal(path.extname('/path.to/file'), ''); +assert.equal(path.extname('/path.to/.file'), ''); +assert.equal(path.extname('/path.to/.file.ext'), '.ext'); +assert.equal(path.extname('/path/to/f.ext'), '.ext'); +assert.equal(path.extname('/path/to/..ext'), '.ext'); +assert.equal(path.extname('file'), ''); +assert.equal(path.extname('file.ext'), '.ext'); +assert.equal(path.extname('.file'), ''); +assert.equal(path.extname('.file.ext'), '.ext'); +assert.equal(path.extname('/file'), ''); +assert.equal(path.extname('/file.ext'), '.ext'); +assert.equal(path.extname('/.file'), ''); +assert.equal(path.extname('/.file.ext'), '.ext'); +assert.equal(path.extname('.path/file.ext'), '.ext'); +assert.equal(path.extname('file.ext.ext'), '.ext'); +assert.equal(path.extname('file.'), '.'); +assert.equal(path.extname('.'), ''); +assert.equal(path.extname('./'), ''); +assert.equal(path.extname('.file.ext'), '.ext'); +assert.equal(path.extname('.file'), ''); +assert.equal(path.extname('.file.'), '.'); +assert.equal(path.extname('.file..'), '.'); +assert.equal(path.extname('..'), ''); +assert.equal(path.extname('../'), ''); +assert.equal(path.extname('..file.ext'), '.ext'); +assert.equal(path.extname('..file'), '.file'); +assert.equal(path.extname('..file.'), '.'); +assert.equal(path.extname('..file..'), '.'); +assert.equal(path.extname('...'), '.'); +assert.equal(path.extname('...ext'), '.ext'); +assert.equal(path.extname('....'), '.'); +assert.equal(path.extname('file.ext/'), ''); + +if (isWindows) { + // On windows, backspace is a path separator. + assert.equal(path.extname('.\\'), ''); + assert.equal(path.extname('..\\'), ''); + assert.equal(path.extname('file.ext\\'), ''); +} else { + // On unix, backspace is a valid name component like any other character. + assert.equal(path.extname('.\\'), ''); + assert.equal(path.extname('..\\'), '.\\'); + assert.equal(path.extname('file.ext\\'), '.ext\\'); +} + +// path.join tests +var failures = []; +var joinTests = + // arguments result + [[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'], + [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'], + [['/foo', '../../../bar'], '/bar'], + [['foo', '../../../bar'], '../../bar'], + [['foo/', '../../../bar'], '../../bar'], + [['foo/x', '../../../bar'], '../bar'], + [['foo/x', './bar'], 'foo/x/bar'], + [['foo/x/', './bar'], 'foo/x/bar'], + [['foo/x/', '.', 'bar'], 'foo/x/bar'], + [['./'], './'], + [['.', './'], './'], + [['.', '.', '.'], '.'], + [['.', './', '.'], '.'], + [['.', '/./', '.'], '.'], + [['.', '/////./', '.'], '.'], + [['.'], '.'], + [['', '.'], '.'], + [['', 'foo'], 'foo'], + [['foo', '/bar'], 'foo/bar'], + [['', '/foo'], '/foo'], + [['', '', '/foo'], '/foo'], + [['', '', 'foo'], 'foo'], + [['foo', ''], 'foo'], + [['foo/', ''], 'foo/'], + [['foo', '', '/bar'], 'foo/bar'], + [['./', '..', '/foo'], '../foo'], + [['./', '..', '..', '/foo'], '../../foo'], + [['.', '..', '..', '/foo'], '../../foo'], + [['', '..', '..', '/foo'], '../../foo'], + [['/'], '/'], + [['/', '.'], '/'], + [['/', '..'], '/'], + [['/', '..', '..'], '/'], + [[''], '.'], + [['', ''], '.'], + [[' /foo'], ' /foo'], + [[' ', 'foo'], ' /foo'], + [[' ', '.'], ' '], + [[' ', '/'], ' /'], + [[' ', ''], ' '], + // filtration of non-strings. + [['x', true, 7, 'y', null, {}], 'x/y'] + ]; +joinTests.forEach(function(test) { + var actual = path.join.apply(path, test[0]); + var expected = isWindows ? test[1].replace(/\//g, '\\') : test[1]; + var message = 'path.join(' + test[0].map(JSON.stringify).join(',') + ')' + + '\n expect=' + JSON.stringify(expected) + + '\n actual=' + JSON.stringify(actual); + if (actual !== expected) failures.push('\n' + message); + // assert.equal(actual, expected, message); +}); +assert.equal(failures.length, 0, failures.join('')); + +// path normalize tests +if (isWindows) { + assert.equal(path.normalize('./fixtures///b/../b/c.js'), + 'fixtures\\b\\c.js'); + assert.equal(path.normalize('/foo/../../../bar'), '\\bar'); + assert.equal(path.normalize('a//b//../b'), 'a\\b'); + assert.equal(path.normalize('a//b//./c'), 'a\\b\\c'); + assert.equal(path.normalize('a//b//.'), 'a\\b'); +} else { + assert.equal(path.normalize('./fixtures///b/../b/c.js'), + 'fixtures/b/c.js'); + assert.equal(path.normalize('/foo/../../../bar'), '/bar'); + assert.equal(path.normalize('a//b//../b'), 'a/b'); + assert.equal(path.normalize('a//b//./c'), 'a/b/c'); + assert.equal(path.normalize('a//b//.'), 'a/b'); +} + +// path.resolve tests +if (isWindows) { + // windows + var resolveTests = + // arguments result + [[['c:/blah\\blah', 'd:/games', 'c:../a'], 'c:\\blah\\a'], + [['c:/ignore', 'd:\\a/b\\c/d', '\\e.exe'], 'd:\\e.exe'], + [['c:/ignore', 'c:/some/file'], 'c:\\some\\file'], + [['d:/ignore', 'd:some/dir//'], 'd:\\ignore\\some\\dir'], + [['.'], process.cwd()], + [['//server/share', '..', 'relative\\'], '\\\\server\\share\\relative']]; +} else { + // Posix + var resolveTests = + // arguments result + [[['/var/lib', '../', 'file/'], '/var/file'], + [['/var/lib', '/../', 'file/'], '/file'], + [['a/b/c/', '../../..'], process.cwd()], + [['.'], process.cwd()], + [['/some/dir', '.', '/absolute/'], '/absolute']]; +} +var failures = []; +resolveTests.forEach(function(test) { + var actual = path.resolve.apply(path, test[0]); + var expected = test[1]; + var message = 'path.resolve(' + test[0].map(JSON.stringify).join(',') + ')' + + '\n expect=' + JSON.stringify(expected) + + '\n actual=' + JSON.stringify(actual); + if (actual !== expected) failures.push('\n' + message); + // assert.equal(actual, expected, message); +}); +assert.equal(failures.length, 0, failures.join('')); + +// path.relative tests +if (isWindows) { + // windows + var relativeTests = + // arguments result + [['c:/blah\\blah', 'd:/games', 'd:\\games'], + ['c:/aaaa/bbbb', 'c:/aaaa', '..'], + ['c:/aaaa/bbbb', 'c:/cccc', '..\\..\\cccc'], + ['c:/aaaa/bbbb', 'c:/aaaa/bbbb', ''], + ['c:/aaaa/bbbb', 'c:/aaaa/cccc', '..\\cccc'], + ['c:/aaaa/', 'c:/aaaa/cccc', 'cccc'], + ['c:/', 'c:\\aaaa\\bbbb', 'aaaa\\bbbb'], + ['c:/aaaa/bbbb', 'd:\\', 'd:\\']]; +} else { + // posix + var relativeTests = + // arguments result + [['/var/lib', '/var', '..'], + ['/var/lib', '/bin', '../../bin'], + ['/var/lib', '/var/lib', ''], + ['/var/lib', '/var/apache', '../apache'], + ['/var/', '/var/lib', 'lib'], + ['/', '/var/lib', 'var/lib']]; +} +var failures = []; +relativeTests.forEach(function(test) { + var actual = path.relative(test[0], test[1]); + var expected = test[2]; + var message = 'path.relative(' + + test.slice(0, 2).map(JSON.stringify).join(',') + + ')' + + '\n expect=' + JSON.stringify(expected) + + '\n actual=' + JSON.stringify(actual); + if (actual !== expected) failures.push('\n' + message); +}); +assert.equal(failures.length, 0, failures.join('')); + diff --git a/test/browsertest/nodetests/simple/test-querystring.js b/test/browsertest/nodetests/simple/test-querystring.js new file mode 100644 index 000000000..44229f8c2 --- /dev/null +++ b/test/browsertest/nodetests/simple/test-querystring.js @@ -0,0 +1,230 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +// test using assert +var qs = require('querystring'); + +// folding block, commented to pass gjslint +// {{{ +// [ wonkyQS, canonicalQS, obj ] +var qsTestCases = [ + ['foo=918854443121279438895193', + 'foo=918854443121279438895193', + {'foo': '918854443121279438895193'}], + ['foo=bar', 'foo=bar', {'foo': 'bar'}], + ['foo=bar&foo=quux', 'foo=bar&foo=quux', {'foo': ['bar', 'quux']}], + ['foo=1&bar=2', 'foo=1&bar=2', {'foo': '1', 'bar': '2'}], + ['my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F', + 'my%20weird%20field=q1!2%22\'w%245%267%2Fz8)%3F', + {'my weird field': 'q1!2"\'w$5&7/z8)?' }], + ['foo%3Dbaz=bar', 'foo%3Dbaz=bar', {'foo=baz': 'bar'}], + ['foo=baz=bar', 'foo=baz%3Dbar', {'foo': 'baz=bar'}], + ['str=foo&arr=1&arr=2&arr=3&somenull=&undef=', + 'str=foo&arr=1&arr=2&arr=3&somenull=&undef=', + { 'str': 'foo', + 'arr': ['1', '2', '3'], + 'somenull': '', + 'undef': ''}], + [' foo = bar ', '%20foo%20=%20bar%20', {' foo ': ' bar '}], + // ['foo=%zx', 'foo=%25zx', {'foo': '%zx'}], + ['foo=%EF%BF%BD', 'foo=%EF%BF%BD', {'foo': '\ufffd' }], + // See: https://github.com/joyent/node/issues/1707 + ['hasOwnProperty=x&toString=foo&valueOf=bar&__defineGetter__=baz', + 'hasOwnProperty=x&toString=foo&valueOf=bar&__defineGetter__=baz', + { hasOwnProperty: 'x', + toString: 'foo', + valueOf: 'bar', + __defineGetter__: 'baz' }] +]; + +// [ wonkyQS, canonicalQS, obj ] +var qsColonTestCases = [ + ['foo:bar', 'foo:bar', {'foo': 'bar'}], + ['foo:bar;foo:quux', 'foo:bar;foo:quux', {'foo': ['bar', 'quux']}], + ['foo:1&bar:2;baz:quux', + 'foo:1%26bar%3A2;baz:quux', + {'foo': '1&bar:2', 'baz': 'quux'}], + ['foo%3Abaz:bar', 'foo%3Abaz:bar', {'foo:baz': 'bar'}], + ['foo:baz:bar', 'foo:baz%3Abar', {'foo': 'baz:bar'}] +]; + +// [wonkyObj, qs, canonicalObj] +var extendedFunction = function() {}; +extendedFunction.prototype = {a: 'b'}; +var qsWeirdObjects = [ + [{regexp: /./g}, 'regexp=', {'regexp': ''}], + [{regexp: new RegExp('.', 'g')}, 'regexp=', {'regexp': ''}], + [{fn: function() {}}, 'fn=', {'fn': ''}], + [{fn: new Function('')}, 'fn=', {'fn': ''}], + [{math: Math}, 'math=', {'math': ''}], + [{e: extendedFunction}, 'e=', {'e': ''}], + [{d: new Date()}, 'd=', {'d': ''}], + [{d: Date}, 'd=', {'d': ''}], + [{f: new Boolean(false), t: new Boolean(true)}, 'f=&t=', {'f': '', 't': ''}], + [{f: false, t: true}, 'f=false&t=true', {'f': 'false', 't': 'true'}], + [{n: null}, 'n=', {'n': ''}], + [{nan: NaN}, 'nan=', {'nan': ''}], + [{inf: Infinity}, 'inf=', {'inf': ''}] +]; +// }}} + +var Script = require('vm').Script; +var foreignObject = Script.runInContext('({"foo": ["bar", "baz"]})', + Script.createContext()); + +var qsNoMungeTestCases = [ + ['', {}], + ['foo=bar&foo=baz', {'foo': ['bar', 'baz']}], + ['foo=bar&foo=baz', foreignObject], + ['blah=burp', {'blah': 'burp'}], + ['gragh=1&gragh=3&goo=2', {'gragh': ['1', '3'], 'goo': '2'}], + ['frappucino=muffin&goat%5B%5D=scone&pond=moose', + {'frappucino': 'muffin', 'goat[]': 'scone', 'pond': 'moose'}], + ['trololol=yes&lololo=no', {'trololol': 'yes', 'lololo': 'no'}] +]; + +assert.strictEqual('918854443121279438895193', + qs.parse('id=918854443121279438895193').id); + +// test that the canonical qs is parsed properly. +qsTestCases.forEach(function(testCase) { + assert.deepEqual(testCase[2], qs.parse(testCase[0])); +}); + +// test that the colon test cases can do the same +qsColonTestCases.forEach(function(testCase) { + assert.deepEqual(testCase[2], qs.parse(testCase[0], ';', ':')); +}); + +// test the weird objects, that they get parsed properly +qsWeirdObjects.forEach(function(testCase) { + assert.deepEqual(testCase[2], qs.parse(testCase[1])); +}); + +qsNoMungeTestCases.forEach(function(testCase) { + assert.deepEqual(testCase[0], qs.stringify(testCase[1], '&', '=', false)); +}); + +// test the nested qs-in-qs case +(function() { + var f = qs.parse('a=b&q=x%3Dy%26y%3Dz'); + f.q = qs.parse(f.q); + assert.deepEqual(f, { a: 'b', q: { x: 'y', y: 'z' } }); +})(); + +// nested in colon +(function() { + var f = qs.parse('a:b;q:x%3Ay%3By%3Az', ';', ':'); + f.q = qs.parse(f.q, ';', ':'); + assert.deepEqual(f, { a: 'b', q: { x: 'y', y: 'z' } }); +})(); + +// now test stringifying + +// basic +qsTestCases.forEach(function(testCase) { + assert.equal(testCase[1], qs.stringify(testCase[2])); +}); + +qsColonTestCases.forEach(function(testCase) { + assert.equal(testCase[1], qs.stringify(testCase[2], ';', ':')); +}); + +qsWeirdObjects.forEach(function(testCase) { + assert.equal(testCase[1], qs.stringify(testCase[0])); +}); + +// nested +var f = qs.stringify({ + a: 'b', + q: qs.stringify({ + x: 'y', + y: 'z' + }) +}); +assert.equal(f, 'a=b&q=x%3Dy%26y%3Dz'); + +assert.doesNotThrow(function() { + qs.parse(undefined); +}); + +// nested in colon +var f = qs.stringify({ + a: 'b', + q: qs.stringify({ + x: 'y', + y: 'z' + }, ';', ':') +}, ';', ':'); +assert.equal(f, 'a:b;q:x%3Ay%3By%3Az'); + + +assert.deepEqual({}, qs.parse()); + + +// Test limiting +assert.equal( + Object.keys(qs.parse('a=1&b=1&c=1', null, null, { maxKeys: 1 })).length, + 1); + +// Test removing limit +function testUnlimitedKeys() { + var query = {}, + url; + + for (var i = 0; i < 2000; i++) query[i] = i; + + url = qs.stringify(query); + + assert.equal( + Object.keys(qs.parse(url, null, null, { maxKeys: 0 })).length, + 2000); +} +testUnlimitedKeys(); + +/* +var b = qs.unescapeBuffer('%d3%f2Ug%1f6v%24%5e%98%cb' + + '%0d%ac%a2%2f%9d%eb%d8%a2%e6'); +// +assert.equal(0xd3, b[0]); +assert.equal(0xf2, b[1]); +assert.equal(0x55, b[2]); +assert.equal(0x67, b[3]); +assert.equal(0x1f, b[4]); +assert.equal(0x36, b[5]); +assert.equal(0x76, b[6]); +assert.equal(0x24, b[7]); +assert.equal(0x5e, b[8]); +assert.equal(0x98, b[9]); +assert.equal(0xcb, b[10]); +assert.equal(0x0d, b[11]); +assert.equal(0xac, b[12]); +assert.equal(0xa2, b[13]); +assert.equal(0x2f, b[14]); +assert.equal(0x9d, b[15]); +assert.equal(0xeb, b[16]); +assert.equal(0xd8, b[17]); +assert.equal(0xa2, b[18]); +assert.equal(0xe6, b[19]); +*/ \ No newline at end of file diff --git a/test/browsertest/nodetests/simple/test-sys.js b/test/browsertest/nodetests/simple/test-sys.js new file mode 100644 index 000000000..6ea33161c --- /dev/null +++ b/test/browsertest/nodetests/simple/test-sys.js @@ -0,0 +1,129 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +assert.equal('0', common.inspect(0)); +assert.equal('1', common.inspect(1)); +assert.equal('false', common.inspect(false)); +assert.equal("''", common.inspect('')); +assert.equal("'hello'", common.inspect('hello')); +assert.equal('[Function]', common.inspect(function() {})); +assert.equal('undefined', common.inspect(undefined)); +assert.equal('null', common.inspect(null)); +assert.equal('/foo(bar\\n)?/gi', common.inspect(/foo(bar\n)?/gi)); +assert.equal(new Date('2010-02-14T12:48:40+01:00').toString(), + common.inspect(new Date('Sun, 14 Feb 2010 11:48:40 GMT'))); + +assert.equal("'\\n\\u0001'", common.inspect('\n\u0001')); + +assert.equal('[]', common.inspect([])); +assert.equal('{}', common.inspect(Object.create([]))); +assert.equal('[ 1, 2 ]', common.inspect([1, 2])); +assert.equal('[ 1, [ 2, 3 ] ]', common.inspect([1, [2, 3]])); + +assert.equal('{}', common.inspect({})); +assert.equal('{ a: 1 }', common.inspect({a: 1})); +assert.equal('{ a: [Function] }', common.inspect({a: function() {}})); +assert.equal('{ a: 1, b: 2 }', common.inspect({a: 1, b: 2})); +assert.equal('{ a: {} }', common.inspect({'a': {}})); +assert.equal('{ a: { b: 2 } }', common.inspect({'a': {'b': 2}})); +assert.equal('{ a: { b: { c: [Object] } } }', + common.inspect({'a': {'b': { 'c': { 'd': 2 }}}})); +assert.equal('{ a: { b: { c: { d: 2 } } } }', + common.inspect({'a': {'b': { 'c': { 'd': 2 }}}}, false, null)); +assert.equal('[ 1, 2, 3, [length]: 3 ]', common.inspect([1, 2, 3], true)); +assert.equal('{ a: [Object] }', + common.inspect({'a': {'b': { 'c': 2}}}, false, 0)); +assert.equal('{ a: { b: [Object] } }', + common.inspect({'a': {'b': { 'c': 2}}}, false, 1)); +assert.equal('{ visible: 1 }', + common.inspect(Object.create({}, + {visible: {value: 1, enumerable: true}, hidden: {value: 2}})) +); + +// Due to the hash seed randomization it's not deterministic the order that +// the following ways this hash is displayed. +// See http://codereview.chromium.org/9124004/ + +var out = common.inspect(Object.create({}, + {visible: {value: 1, enumerable: true}, hidden: {value: 2}}), true); +if (out !== '{ [hidden]: 2, visible: 1 }' && + out !== '{ visible: 1, [hidden]: 2 }') { + assert.ok(false); +} + + +// Objects without prototype +var out = common.inspect(Object.create(null, + { name: {value: 'Tim', enumerable: true}, + hidden: {value: 'secret'}}), true); +if (out !== "{ [hidden]: 'secret', name: 'Tim' }" && + out !== "{ name: 'Tim', [hidden]: 'secret' }") { + assert(false); +} + + +assert.equal('{ name: \'Tim\' }', + common.inspect(Object.create(null, + {name: {value: 'Tim', enumerable: true}, + hidden: {value: 'secret'}})) +); + + +// Dynamic properties +assert.equal('{ readonly: [Getter] }', + common.inspect({get readonly() {}})); + +assert.equal('{ readwrite: [Getter/Setter] }', + common.inspect({get readwrite() {},set readwrite(val) {}})); + +assert.equal('{ writeonly: [Setter] }', + common.inspect({set writeonly(val) {}})); + +var value = {}; +value['a'] = value; +assert.equal('{ a: [Circular] }', common.inspect(value)); + +// Array with dynamic properties +value = [1, 2, 3]; +value.__defineGetter__('growingLength', function() { + this.push(true); return this.length; +}); +assert.equal('[ 1, 2, 3, growingLength: [Getter] ]', common.inspect(value)); + +// Function with properties +value = function() {}; +value.aprop = 42; +assert.equal('{ [Function] aprop: 42 }', common.inspect(value)); + +// Regular expressions with properties +value = /123/ig; +value.aprop = 42; +assert.equal('{ /123/gi aprop: 42 }', common.inspect(value)); + +// Dates with properties +value = new Date('Sun, 14 Feb 2010 11:48:40 GMT'); +value.aprop = 42; +assert.equal('{ Sun, 14 Feb 2010 11:48:40 GMT aprop: 42 }', + common.inspect(value) +); diff --git a/test/browsertest/nodetests/simple/test-timers-zero-timeout.js b/test/browsertest/nodetests/simple/test-timers-zero-timeout.js new file mode 100644 index 000000000..eb43f183f --- /dev/null +++ b/test/browsertest/nodetests/simple/test-timers-zero-timeout.js @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +// https://github.com/joyent/node/issues/2079 - zero timeout drops extra args +(function() { + var ncalled = 0; + + setTimeout(f, 0, 'foo', 'bar', 'baz'); + var timer = setTimeout(function() {}, 0); + + function f(a, b, c) { + assert.equal(a, 'foo'); + assert.equal(b, 'bar'); + assert.equal(c, 'baz'); + ncalled++; + } + + process.on('exit', function() { + assert.equal(ncalled, 1); + }); +})(); + +(function() { + var ncalled = 0; + + var iv = setInterval(f, 0, 'foo', 'bar', 'baz'); + + function f(a, b, c) { + assert.equal(a, 'foo'); + assert.equal(b, 'bar'); + assert.equal(c, 'baz'); + if (++ncalled == 3) clearTimeout(iv); + } + + process.on('exit', function() { + assert.equal(ncalled, 3); + }); +})(); diff --git a/test/browsertest/nodetests/simple/test-timers.js b/test/browsertest/nodetests/simple/test-timers.js new file mode 100644 index 000000000..51e251e17 --- /dev/null +++ b/test/browsertest/nodetests/simple/test-timers.js @@ -0,0 +1,71 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var inputs = [ + undefined, + null, + true, + false, + '', + [], + {}, + NaN, + +Infinity, + -Infinity, + (1.0 / 0.0), // sanity check + parseFloat('x'), // NaN + -10, + -1, + -0.5, + -0.0, + 0, + 0.0, + 0.5, + 1, + 1.0, + 10, + 2147483648 // browser behaviour: timeouts > 2^31-1 run on next tick +]; + +var timeouts = []; +var intervals = []; + +inputs.forEach(function(value, index) { + setTimeout(function() { + timeouts[index] = true; + }, value); + + var handle = setInterval(function() { + clearInterval(handle); // disarm timer or we'll never finish + intervals[index] = true; + }, value); +}); + +process.on('exit', function() { + // assert that all timers have run + inputs.forEach(function(value, index) { + assert.equal(true, timeouts[index]); + assert.equal(true, intervals[index]); + }); +}); diff --git a/test/browsertest/nodetests/simple/test-url.js b/test/browsertest/nodetests/simple/test-url.js new file mode 100644 index 000000000..7287d0f57 --- /dev/null +++ b/test/browsertest/nodetests/simple/test-url.js @@ -0,0 +1,1229 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var url = require('url'), + util = require('util'); + +// URLs to parse, and expected data +// { url : parsed } +var parseTests = { + '//some_path' : { + 'href': '//some_path', + 'pathname': '//some_path', + 'path': '//some_path' + }, + 'HTTP://www.example.com/' : { + 'href': 'http://www.example.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'www.example.com', + 'hostname': 'www.example.com', + 'pathname': '/', + 'path': '/' + }, + 'http://www.ExAmPlE.com/' : { + 'href': 'http://www.example.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'www.example.com', + 'hostname': 'www.example.com', + 'pathname': '/', + 'path': '/' + }, + 'http://user:pw@www.ExAmPlE.com/' : { + 'href': 'http://user:pw@www.example.com/', + 'protocol': 'http:', + 'slashes': true, + 'auth': 'user:pw', + 'host': 'www.example.com', + 'hostname': 'www.example.com', + 'pathname': '/', + 'path': '/' + }, + 'http://USER:PW@www.ExAmPlE.com/' : { + 'href': 'http://USER:PW@www.example.com/', + 'protocol': 'http:', + 'slashes': true, + 'auth': 'USER:PW', + 'host': 'www.example.com', + 'hostname': 'www.example.com', + 'pathname': '/', + 'path': '/' + }, + 'http://user@www.example.com/' : { + 'href': 'http://user@www.example.com/', + 'protocol': 'http:', + 'slashes': true, + 'auth': 'user', + 'host': 'www.example.com', + 'hostname': 'www.example.com', + 'pathname': '/', + 'path': '/' + }, + 'http://user%3Apw@www.example.com/' : { + 'href': 'http://user:pw@www.example.com/', + 'protocol': 'http:', + 'slashes': true, + 'auth': 'user:pw', + 'host': 'www.example.com', + 'hostname': 'www.example.com', + 'pathname': '/', + 'path': '/' + }, + 'http://x.com/path?that\'s#all, folks' : { + 'href': 'http://x.com/path?that%27s#all,', + 'protocol': 'http:', + 'slashes': true, + 'host': 'x.com', + 'hostname': 'x.com', + 'search': '?that%27s', + 'query': 'that%27s', + 'pathname': '/path', + 'hash': '#all,', + 'path': '/path?that%27s' + }, + 'HTTP://X.COM/Y' : { + 'href': 'http://x.com/Y', + 'protocol': 'http:', + 'slashes': true, + 'host': 'x.com', + 'hostname': 'x.com', + 'pathname': '/Y', + 'path': '/Y' + }, + // an unexpected invalid char in the hostname. + 'HtTp://x.y.cOm*a/b/c?d=e#f gi' : { + 'href': 'http://x.y.com/*a/b/c?d=e#f', + 'protocol': 'http:', + 'slashes': true, + 'host': 'x.y.com', + 'hostname': 'x.y.com', + 'pathname': '/*a/b/c', + 'search': '?d=e', + 'query': 'd=e', + 'hash': '#f', + 'path': '/*a/b/c?d=e' + }, + // make sure that we don't accidentally lcast the path parts. + 'HtTp://x.y.cOm*A/b/c?d=e#f gi' : { + 'href': 'http://x.y.com/*A/b/c?d=e#f', + 'protocol': 'http:', + 'slashes': true, + 'host': 'x.y.com', + 'hostname': 'x.y.com', + 'pathname': '/*A/b/c', + 'search': '?d=e', + 'query': 'd=e', + 'hash': '#f', + 'path': '/*A/b/c?d=e' + }, + 'http://x...y...#p': { + 'href': 'http://x...y.../#p', + 'protocol': 'http:', + 'slashes': true, + 'host': 'x...y...', + 'hostname': 'x...y...', + 'hash': '#p', + 'pathname': '/', + 'path': '/' + }, + 'http://x/p/"quoted"': { + 'href': 'http://x/p/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'x', + 'hostname': 'x', + 'pathname': '/p/', + 'path': '/p/' + }, + ' Is a URL!': { + 'href': 'http://goo.corn/bread', + 'protocol': 'http:', + 'slashes': true, + 'host': 'goo.corn', + 'hostname': 'goo.corn', + 'pathname': '/bread', + 'path': '/bread' + }, + 'http://www.narwhaljs.org/blog/categories?id=news' : { + 'href': 'http://www.narwhaljs.org/blog/categories?id=news', + 'protocol': 'http:', + 'slashes': true, + 'host': 'www.narwhaljs.org', + 'hostname': 'www.narwhaljs.org', + 'search': '?id=news', + 'query': 'id=news', + 'pathname': '/blog/categories', + 'path': '/blog/categories?id=news' + }, + 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=' : { + 'href': 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', + 'protocol': 'http:', + 'slashes': true, + 'host': 'mt0.google.com', + 'hostname': 'mt0.google.com', + 'pathname': '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', + 'path': '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=' + }, + 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' : { + 'href': 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api' + + '&x=2&y=2&z=3&s=', + 'protocol': 'http:', + 'slashes': true, + 'host': 'mt0.google.com', + 'hostname': 'mt0.google.com', + 'search': '???&hl=en&src=api&x=2&y=2&z=3&s=', + 'query': '??&hl=en&src=api&x=2&y=2&z=3&s=', + 'pathname': '/vt/lyrs=m@114', + 'path': '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' + }, + 'http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=': + { + 'href': 'http://user:pass@mt0.google.com/vt/lyrs=m@114???' + + '&hl=en&src=api&x=2&y=2&z=3&s=', + 'protocol': 'http:', + 'slashes': true, + 'host': 'mt0.google.com', + 'auth': 'user:pass', + 'hostname': 'mt0.google.com', + 'search': '???&hl=en&src=api&x=2&y=2&z=3&s=', + 'query': '??&hl=en&src=api&x=2&y=2&z=3&s=', + 'pathname': '/vt/lyrs=m@114', + 'path': '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' + }, + 'file:///etc/passwd' : { + 'href': 'file:///etc/passwd', + 'slashes': true, + 'protocol': 'file:', + 'pathname': '/etc/passwd', + 'hostname': '', + 'host': '', + 'path': '/etc/passwd' + }, + 'file://localhost/etc/passwd' : { + 'href': 'file://localhost/etc/passwd', + 'protocol': 'file:', + 'slashes': true, + 'pathname': '/etc/passwd', + 'hostname': 'localhost', + 'host': 'localhost', + 'path': '/etc/passwd' + }, + 'file://foo/etc/passwd' : { + 'href': 'file://foo/etc/passwd', + 'protocol': 'file:', + 'slashes': true, + 'pathname': '/etc/passwd', + 'hostname': 'foo', + 'host': 'foo', + 'path': '/etc/passwd' + }, + 'file:///etc/node/' : { + 'href': 'file:///etc/node/', + 'slashes': true, + 'protocol': 'file:', + 'pathname': '/etc/node/', + 'hostname': '', + 'host': '', + 'path': '/etc/node/' + }, + 'file://localhost/etc/node/' : { + 'href': 'file://localhost/etc/node/', + 'protocol': 'file:', + 'slashes': true, + 'pathname': '/etc/node/', + 'hostname': 'localhost', + 'host': 'localhost', + 'path': '/etc/node/' + }, + 'file://foo/etc/node/' : { + 'href': 'file://foo/etc/node/', + 'protocol': 'file:', + 'slashes': true, + 'pathname': '/etc/node/', + 'hostname': 'foo', + 'host': 'foo', + 'path': '/etc/node/' + }, + 'http:/baz/../foo/bar' : { + 'href': 'http:/baz/../foo/bar', + 'protocol': 'http:', + 'pathname': '/baz/../foo/bar', + 'path': '/baz/../foo/bar' + }, + 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag' : { + 'href': 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag', + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com:8000', + 'auth': 'user:pass', + 'port': '8000', + 'hostname': 'example.com', + 'hash': '#frag', + 'search': '?baz=quux', + 'query': 'baz=quux', + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' + }, + '//user:pass@example.com:8000/foo/bar?baz=quux#frag' : { + 'href': '//user:pass@example.com:8000/foo/bar?baz=quux#frag', + 'slashes': true, + 'host': 'example.com:8000', + 'auth': 'user:pass', + 'port': '8000', + 'hostname': 'example.com', + 'hash': '#frag', + 'search': '?baz=quux', + 'query': 'baz=quux', + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' + }, + '/foo/bar?baz=quux#frag' : { + 'href': '/foo/bar?baz=quux#frag', + 'hash': '#frag', + 'search': '?baz=quux', + 'query': 'baz=quux', + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' + }, + 'http:/foo/bar?baz=quux#frag' : { + 'href': 'http:/foo/bar?baz=quux#frag', + 'protocol': 'http:', + 'hash': '#frag', + 'search': '?baz=quux', + 'query': 'baz=quux', + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' + }, + 'mailto:foo@bar.com?subject=hello' : { + 'href': 'mailto:foo@bar.com?subject=hello', + 'protocol': 'mailto:', + 'host': 'bar.com', + 'auth' : 'foo', + 'hostname' : 'bar.com', + 'search': '?subject=hello', + 'query': 'subject=hello', + 'path': '?subject=hello' + }, + 'javascript:alert(\'hello\');' : { + 'href': 'javascript:alert(\'hello\');', + 'protocol': 'javascript:', + 'pathname': 'alert(\'hello\');', + 'path': 'alert(\'hello\');' + }, + 'xmpp:isaacschlueter@jabber.org' : { + 'href': 'xmpp:isaacschlueter@jabber.org', + 'protocol': 'xmpp:', + 'host': 'jabber.org', + 'auth': 'isaacschlueter', + 'hostname': 'jabber.org' + }, + 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar' : { + 'href' : 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar', + 'protocol' : 'http:', + 'slashes': true, + 'host' : '127.0.0.1:8080', + 'auth' : 'atpass:foo@bar', + 'hostname' : '127.0.0.1', + 'port' : '8080', + 'pathname': '/path', + 'search' : '?search=foo', + 'query' : 'search=foo', + 'hash' : '#bar', + 'path': '/path?search=foo' + }, + 'svn+ssh://foo/bar': { + 'href': 'svn+ssh://foo/bar', + 'host': 'foo', + 'hostname': 'foo', + 'protocol': 'svn+ssh:', + 'pathname': '/bar', + 'path': '/bar', + 'slashes': true + }, + 'dash-test://foo/bar': { + 'href': 'dash-test://foo/bar', + 'host': 'foo', + 'hostname': 'foo', + 'protocol': 'dash-test:', + 'pathname': '/bar', + 'path': '/bar', + 'slashes': true + }, + 'dash-test:foo/bar': { + 'href': 'dash-test:foo/bar', + 'host': 'foo', + 'hostname': 'foo', + 'protocol': 'dash-test:', + 'pathname': '/bar', + 'path': '/bar' + }, + 'dot.test://foo/bar': { + 'href': 'dot.test://foo/bar', + 'host': 'foo', + 'hostname': 'foo', + 'protocol': 'dot.test:', + 'pathname': '/bar', + 'path': '/bar', + 'slashes': true + }, + 'dot.test:foo/bar': { + 'href': 'dot.test:foo/bar', + 'host': 'foo', + 'hostname': 'foo', + 'protocol': 'dot.test:', + 'pathname': '/bar', + 'path': '/bar' + }, + // IDNA tests + 'http://www.日本語.com/' : { + 'href': 'http://www.xn--wgv71a119e.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'www.xn--wgv71a119e.com', + 'hostname': 'www.xn--wgv71a119e.com', + 'pathname': '/', + 'path': '/' + }, + 'http://example.Bücher.com/' : { + 'href': 'http://example.xn--bcher-kva.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.xn--bcher-kva.com', + 'hostname': 'example.xn--bcher-kva.com', + 'pathname': '/', + 'path': '/' + }, + 'http://www.Äffchen.com/' : { + 'href': 'http://www.xn--ffchen-9ta.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'www.xn--ffchen-9ta.com', + 'hostname': 'www.xn--ffchen-9ta.com', + 'pathname': '/', + 'path': '/' + }, + 'http://www.Äffchen.cOm*A/b/c?d=e#f gi' : { + 'href': 'http://www.xn--ffchen-9ta.com/*A/b/c?d=e#f', + 'protocol': 'http:', + 'slashes': true, + 'host': 'www.xn--ffchen-9ta.com', + 'hostname': 'www.xn--ffchen-9ta.com', + 'pathname': '/*A/b/c', + 'search': '?d=e', + 'query': 'd=e', + 'hash': '#f', + 'path': '/*A/b/c?d=e' + }, + 'http://SÉLIER.COM/' : { + 'href': 'http://xn--slier-bsa.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'xn--slier-bsa.com', + 'hostname': 'xn--slier-bsa.com', + 'pathname': '/', + 'path': '/' + }, + 'http://ليهمابتكلموشعربي؟.ي؟/' : { + 'href': 'http://xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', + 'hostname': 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', + 'pathname': '/', + 'path': '/' + }, + 'http://➡.ws/➡' : { + 'href': 'http://xn--hgi.ws/➡', + 'protocol': 'http:', + 'slashes': true, + 'host': 'xn--hgi.ws', + 'hostname': 'xn--hgi.ws', + 'pathname': '/➡', + 'path': '/➡' + }, + 'http://bucket_name.s3.amazonaws.com/image.jpg': { + protocol: 'http:', + 'slashes': true, + slashes: true, + host: 'bucket_name.s3.amazonaws.com', + hostname: 'bucket_name.s3.amazonaws.com', + pathname: '/image.jpg', + href: 'http://bucket_name.s3.amazonaws.com/image.jpg', + 'path': '/image.jpg' + }, + 'git+http://github.com/joyent/node.git': { + protocol: 'git+http:', + slashes: true, + host: 'github.com', + hostname: 'github.com', + pathname: '/joyent/node.git', + path: '/joyent/node.git', + href: 'git+http://github.com/joyent/node.git' + }, + //if local1@domain1 is uses as a relative URL it may + //be parse into auth@hostname, but here there is no + //way to make it work in url.parse, I add the test to be explicit + 'local1@domain1': { + 'pathname': 'local1@domain1', + 'path': 'local1@domain1', + 'href': 'local1@domain1' + }, + //While this may seem counter-intuitive, a browser will parse + // as a path. + 'www.example.com' : { + 'href': 'www.example.com', + 'pathname': 'www.example.com', + 'path': 'www.example.com' + }, + // ipv6 support + '[fe80::1]': { + 'href': '[fe80::1]', + 'pathname': '[fe80::1]', + 'path': '[fe80::1]' + }, + 'coap://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]': { + 'protocol': 'coap:', + 'slashes': true, + 'host': '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]', + 'hostname': 'fedc:ba98:7654:3210:fedc:ba98:7654:3210', + 'href': 'coap://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]/', + 'pathname': '/', + 'path': '/' + }, + 'coap://[1080:0:0:0:8:800:200C:417A]:61616/': { + 'protocol': 'coap:', + 'slashes': true, + 'host': '[1080:0:0:0:8:800:200c:417a]:61616', + 'port': '61616', + 'hostname': '1080:0:0:0:8:800:200c:417a', + 'href': 'coap://[1080:0:0:0:8:800:200c:417a]:61616/', + 'pathname': '/', + 'path': '/' + }, + 'http://user:password@[3ffe:2a00:100:7031::1]:8080': { + 'protocol': 'http:', + 'slashes': true, + 'auth': 'user:password', + 'host': '[3ffe:2a00:100:7031::1]:8080', + 'port': '8080', + 'hostname': '3ffe:2a00:100:7031::1', + 'href': 'http://user:password@[3ffe:2a00:100:7031::1]:8080/', + 'pathname': '/', + 'path': '/' + }, + 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature': { + 'protocol': 'coap:', + 'slashes': true, + 'auth': 'u:p', + 'host': '[::192.9.5.5]:61616', + 'port': '61616', + 'hostname': '::192.9.5.5', + 'href': 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature', + 'search': '?n=Temperature', + 'query': 'n=Temperature', + 'pathname': '/.well-known/r', + 'path': '/.well-known/r?n=Temperature' + }, + // empty port + 'http://example.com:': { + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com', + 'hostname': 'example.com', + 'href': 'http://example.com/', + 'pathname': '/', + 'path': '/' + }, + 'http://example.com:/a/b.html': { + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com', + 'hostname': 'example.com', + 'href': 'http://example.com/a/b.html', + 'pathname': '/a/b.html', + 'path': '/a/b.html' + }, + 'http://example.com:?a=b': { + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com', + 'hostname': 'example.com', + 'href': 'http://example.com/?a=b', + 'search': '?a=b', + 'query': 'a=b', + 'pathname': '/', + 'path': '/?a=b' + }, + 'http://example.com:#abc': { + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com', + 'hostname': 'example.com', + 'href': 'http://example.com/#abc', + 'hash': '#abc', + 'pathname': '/', + 'path': '/' + }, + 'http://[fe80::1]:/a/b?a=b#abc': { + 'protocol': 'http:', + 'slashes': true, + 'host': '[fe80::1]', + 'hostname': 'fe80::1', + 'href': 'http://[fe80::1]/a/b?a=b#abc', + 'search': '?a=b', + 'query': 'a=b', + 'hash': '#abc', + 'pathname': '/a/b', + 'path': '/a/b?a=b' + } +}; + +for (var u in parseTests) { + var actual = url.parse(u), + expected = parseTests[u]; + + assert.deepEqual(actual, expected); + + var expected = parseTests[u].href, + actual = url.format(parseTests[u]); + + assert.equal(actual, expected, + 'format(' + u + ') == ' + u + '\nactual:' + actual); +} + +var parseTestsWithQueryString = { + '/foo/bar?baz=quux#frag' : { + 'href': '/foo/bar?baz=quux#frag', + 'hash': '#frag', + 'search': '?baz=quux', + 'query': { + 'baz': 'quux' + }, + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' + }, + 'http://example.com' : { + 'href': 'http://example.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com', + 'hostname': 'example.com', + 'query': {}, + 'search': '', + 'pathname': '/', + 'path': '/' + } +}; +for (var u in parseTestsWithQueryString) { + var actual = url.parse(u, true); + var expected = parseTestsWithQueryString[u]; + + assert.deepEqual(actual, expected); +} + +// some extra formatting tests, just to verify +// that it'll format slightly wonky content to a valid url. +var formatTests = { + 'http://example.com?' : { + 'href': 'http://example.com/?', + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com', + 'hostname': 'example.com', + 'search': '?', + 'query': {}, + 'pathname': '/' + }, + 'http://example.com?foo=bar#frag' : { + 'href': 'http://example.com/?foo=bar#frag', + 'protocol': 'http:', + 'host': 'example.com', + 'hostname': 'example.com', + 'hash': '#frag', + 'search': '?foo=bar', + 'query': 'foo=bar', + 'pathname': '/' + }, + 'http://example.com?foo=@bar#frag' : { + 'href': 'http://example.com/?foo=@bar#frag', + 'protocol': 'http:', + 'host': 'example.com', + 'hostname': 'example.com', + 'hash': '#frag', + 'search': '?foo=@bar', + 'query': 'foo=@bar', + 'pathname': '/' + }, + 'http://example.com?foo=/bar/#frag' : { + 'href': 'http://example.com/?foo=/bar/#frag', + 'protocol': 'http:', + 'host': 'example.com', + 'hostname': 'example.com', + 'hash': '#frag', + 'search': '?foo=/bar/', + 'query': 'foo=/bar/', + 'pathname': '/' + }, + 'http://example.com?foo=?bar/#frag' : { + 'href': 'http://example.com/?foo=?bar/#frag', + 'protocol': 'http:', + 'host': 'example.com', + 'hostname': 'example.com', + 'hash': '#frag', + 'search': '?foo=?bar/', + 'query': 'foo=?bar/', + 'pathname': '/' + }, + 'http://example.com#frag=?bar/#frag' : { + 'href': 'http://example.com/#frag=?bar/#frag', + 'protocol': 'http:', + 'host': 'example.com', + 'hostname': 'example.com', + 'hash': '#frag=?bar/#frag', + 'pathname': '/' + }, + 'http://google.com" onload="alert(42)/' : { + 'href': 'http://google.com/', + 'protocol': 'http:', + 'host': 'google.com', + 'pathname': '/' + }, + 'http://a.com/a/b/c?s#h' : { + 'href': 'http://a.com/a/b/c?s#h', + 'protocol': 'http', + 'host': 'a.com', + 'pathname': 'a/b/c', + 'hash': 'h', + 'search': 's' + }, + 'xmpp:isaacschlueter@jabber.org' : { + 'href': 'xmpp:isaacschlueter@jabber.org', + 'protocol': 'xmpp:', + 'host': 'jabber.org', + 'auth': 'isaacschlueter', + 'hostname': 'jabber.org' + }, + 'http://atpass:foo%40bar@127.0.0.1/' : { + 'href': 'http://atpass:foo%40bar@127.0.0.1/', + 'auth': 'atpass:foo@bar', + 'hostname': '127.0.0.1', + 'protocol': 'http:', + 'pathname': '/' + }, + 'http://atslash%2F%40:%2F%40@foo/' : { + 'href': 'http://atslash%2F%40:%2F%40@foo/', + 'auth': 'atslash/@:/@', + 'hostname': 'foo', + 'protocol': 'http:', + 'pathname': '/' + }, + 'svn+ssh://foo/bar': { + 'href': 'svn+ssh://foo/bar', + 'hostname': 'foo', + 'protocol': 'svn+ssh:', + 'pathname': '/bar', + 'slashes': true + }, + 'dash-test://foo/bar': { + 'href': 'dash-test://foo/bar', + 'hostname': 'foo', + 'protocol': 'dash-test:', + 'pathname': '/bar', + 'slashes': true + }, + 'dash-test:foo/bar': { + 'href': 'dash-test:foo/bar', + 'hostname': 'foo', + 'protocol': 'dash-test:', + 'pathname': '/bar' + }, + 'dot.test://foo/bar': { + 'href': 'dot.test://foo/bar', + 'hostname': 'foo', + 'protocol': 'dot.test:', + 'pathname': '/bar', + 'slashes': true + }, + 'dot.test:foo/bar': { + 'href': 'dot.test:foo/bar', + 'hostname': 'foo', + 'protocol': 'dot.test:', + 'pathname': '/bar' + }, + // ipv6 support + 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature': { + 'href': 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature', + 'protocol': 'coap:', + 'auth': 'u:p', + 'hostname': '::1', + 'port': '61616', + 'pathname': '/.well-known/r', + 'search': 'n=Temperature' + }, + 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton': { + 'href': 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton', + 'protocol': 'coap', + 'host': '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616', + 'pathname': '/s/stopButton' + } +}; +for (var u in formatTests) { + var expect = formatTests[u].href; + delete formatTests[u].href; + var actual = url.format(u); + var actualObj = url.format(formatTests[u]); + assert.equal(actual, expect, + 'wonky format(' + u + ') == ' + expect + + '\nactual:' + actual); + assert.equal(actualObj, expect, + 'wonky format(' + JSON.stringify(formatTests[u]) + + ') == ' + expect + + '\nactual: ' + actualObj); +} + +/* + [from, path, expected] +*/ +var relativeTests = [ + ['/foo/bar/baz', 'quux', '/foo/bar/quux'], + ['/foo/bar/baz', 'quux/asdf', '/foo/bar/quux/asdf'], + ['/foo/bar/baz', 'quux/baz', '/foo/bar/quux/baz'], + ['/foo/bar/baz', '../quux/baz', '/foo/quux/baz'], + ['/foo/bar/baz', '/bar', '/bar'], + ['/foo/bar/baz/', 'quux', '/foo/bar/baz/quux'], + ['/foo/bar/baz/', 'quux/baz', '/foo/bar/baz/quux/baz'], + ['/foo/bar/baz', '../../../../../../../../quux/baz', '/quux/baz'], + ['/foo/bar/baz', '../../../../../../../quux/baz', '/quux/baz'], + ['foo/bar', '../../../baz', '../../baz'], + ['foo/bar/', '../../../baz', '../baz'], + ['http://example.com/b//c//d;p?q#blarg', 'https:#hash2', 'https:///#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'https:/p/a/t/h?s#hash2', + 'https://p/a/t/h?s#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'https://u:p@h.com/p/a/t/h?s#hash2', + 'https://u:p@h.com/p/a/t/h?s#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'https:/a/b/c/d', + 'https://a/b/c/d'], + ['http://example.com/b//c//d;p?q#blarg', + 'http:#hash2', + 'http://example.com/b//c//d;p?q#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'http:/p/a/t/h?s#hash2', + 'http://example.com/p/a/t/h?s#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'http://u:p@h.com/p/a/t/h?s#hash2', + 'http://u:p@h.com/p/a/t/h?s#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'http:/a/b/c/d', + 'http://example.com/a/b/c/d'], + ['/foo/bar/baz', '/../etc/passwd', '/etc/passwd'] +]; +relativeTests.forEach(function(relativeTest) { + var a = url.resolve(relativeTest[0], relativeTest[1]), + e = relativeTest[2]; + assert.equal(a, e, + 'resolve(' + [relativeTest[0], relativeTest[1]] + ') == ' + e + + '\n actual=' + a); +}); + + +// https://github.com/joyent/node/issues/568 +[ + undefined, + null, + true, + false, + 0.0, + 0, + [], + {} +].forEach(function(val) { + assert.throws(function() { url.parse(val); }, TypeError); +}); + + +// +// Tests below taken from Chiron +// http://code.google.com/p/chironjs/source/browse/trunk/src/test/http/url.js +// +// Copyright (c) 2002-2008 Kris Kowal +// used with permission under MIT License +// +// Changes marked with @isaacs + +var bases = [ + 'http://a/b/c/d;p?q', + 'http://a/b/c/d;p?q=1/2', + 'http://a/b/c/d;p=1/2?q', + 'fred:///s//a/b/c', + 'http:///s//a/b/c' +]; + +//[to, from, result] +var relativeTests2 = [ + // http://lists.w3.org/Archives/Public/uri/2004Feb/0114.html + ['../c', 'foo:a/b', 'foo:c'], + ['foo:.', 'foo:a', 'foo:'], + ['/foo/../../../bar', 'zz:abc', 'zz:/bar'], + ['/foo/../bar', 'zz:abc', 'zz:/bar'], + // @isaacs Disagree. Not how web browsers resolve this. + ['foo/../../../bar', 'zz:abc', 'zz:bar'], + // ['foo/../../../bar', 'zz:abc', 'zz:../../bar'], // @isaacs Added + ['foo/../bar', 'zz:abc', 'zz:bar'], + ['zz:.', 'zz:abc', 'zz:'], + ['/.', bases[0], 'http://a/'], + ['/.foo', bases[0], 'http://a/.foo'], + ['.foo', bases[0], 'http://a/b/c/.foo'], + + // http://gbiv.com/protocols/uri/test/rel_examples1.html + // examples from RFC 2396 + ['g:h', bases[0], 'g:h'], + ['g', bases[0], 'http://a/b/c/g'], + ['./g', bases[0], 'http://a/b/c/g'], + ['g/', bases[0], 'http://a/b/c/g/'], + ['/g', bases[0], 'http://a/g'], + ['//g', bases[0], 'http://g/'], + // changed with RFC 2396bis + //('?y', bases[0], 'http://a/b/c/d;p?y'], + ['?y', bases[0], 'http://a/b/c/d;p?y'], + ['g?y', bases[0], 'http://a/b/c/g?y'], + // changed with RFC 2396bis + //('#s', bases[0], CURRENT_DOC_URI + '#s'], + ['#s', bases[0], 'http://a/b/c/d;p?q#s'], + ['g#s', bases[0], 'http://a/b/c/g#s'], + ['g?y#s', bases[0], 'http://a/b/c/g?y#s'], + [';x', bases[0], 'http://a/b/c/;x'], + ['g;x', bases[0], 'http://a/b/c/g;x'], + ['g;x?y#s' , bases[0], 'http://a/b/c/g;x?y#s'], + // changed with RFC 2396bis + //('', bases[0], CURRENT_DOC_URI], + ['', bases[0], 'http://a/b/c/d;p?q'], + ['.', bases[0], 'http://a/b/c/'], + ['./', bases[0], 'http://a/b/c/'], + ['..', bases[0], 'http://a/b/'], + ['../', bases[0], 'http://a/b/'], + ['../g', bases[0], 'http://a/b/g'], + ['../..', bases[0], 'http://a/'], + ['../../', bases[0], 'http://a/'], + ['../../g' , bases[0], 'http://a/g'], + ['../../../g', bases[0], ('http://a/../g', 'http://a/g')], + ['../../../../g', bases[0], ('http://a/../../g', 'http://a/g')], + // changed with RFC 2396bis + //('/./g', bases[0], 'http://a/./g'], + ['/./g', bases[0], 'http://a/g'], + // changed with RFC 2396bis + //('/../g', bases[0], 'http://a/../g'], + ['/../g', bases[0], 'http://a/g'], + ['g.', bases[0], 'http://a/b/c/g.'], + ['.g', bases[0], 'http://a/b/c/.g'], + ['g..', bases[0], 'http://a/b/c/g..'], + ['..g', bases[0], 'http://a/b/c/..g'], + ['./../g', bases[0], 'http://a/b/g'], + ['./g/.', bases[0], 'http://a/b/c/g/'], + ['g/./h', bases[0], 'http://a/b/c/g/h'], + ['g/../h', bases[0], 'http://a/b/c/h'], + ['g;x=1/./y', bases[0], 'http://a/b/c/g;x=1/y'], + ['g;x=1/../y', bases[0], 'http://a/b/c/y'], + ['g?y/./x', bases[0], 'http://a/b/c/g?y/./x'], + ['g?y/../x', bases[0], 'http://a/b/c/g?y/../x'], + ['g#s/./x', bases[0], 'http://a/b/c/g#s/./x'], + ['g#s/../x', bases[0], 'http://a/b/c/g#s/../x'], + ['http:g', bases[0], ('http:g', 'http://a/b/c/g')], + ['http:', bases[0], ('http:', bases[0])], + // not sure where this one originated + ['/a/b/c/./../../g', bases[0], 'http://a/a/g'], + + // http://gbiv.com/protocols/uri/test/rel_examples2.html + // slashes in base URI's query args + ['g', bases[1], 'http://a/b/c/g'], + ['./g', bases[1], 'http://a/b/c/g'], + ['g/', bases[1], 'http://a/b/c/g/'], + ['/g', bases[1], 'http://a/g'], + ['//g', bases[1], 'http://g/'], + // changed in RFC 2396bis + //('?y', bases[1], 'http://a/b/c/?y'], + ['?y', bases[1], 'http://a/b/c/d;p?y'], + ['g?y', bases[1], 'http://a/b/c/g?y'], + ['g?y/./x' , bases[1], 'http://a/b/c/g?y/./x'], + ['g?y/../x', bases[1], 'http://a/b/c/g?y/../x'], + ['g#s', bases[1], 'http://a/b/c/g#s'], + ['g#s/./x' , bases[1], 'http://a/b/c/g#s/./x'], + ['g#s/../x', bases[1], 'http://a/b/c/g#s/../x'], + ['./', bases[1], 'http://a/b/c/'], + ['../', bases[1], 'http://a/b/'], + ['../g', bases[1], 'http://a/b/g'], + ['../../', bases[1], 'http://a/'], + ['../../g' , bases[1], 'http://a/g'], + + // http://gbiv.com/protocols/uri/test/rel_examples3.html + // slashes in path params + // all of these changed in RFC 2396bis + ['g', bases[2], 'http://a/b/c/d;p=1/g'], + ['./g', bases[2], 'http://a/b/c/d;p=1/g'], + ['g/', bases[2], 'http://a/b/c/d;p=1/g/'], + ['g?y', bases[2], 'http://a/b/c/d;p=1/g?y'], + [';x', bases[2], 'http://a/b/c/d;p=1/;x'], + ['g;x', bases[2], 'http://a/b/c/d;p=1/g;x'], + ['g;x=1/./y', bases[2], 'http://a/b/c/d;p=1/g;x=1/y'], + ['g;x=1/../y', bases[2], 'http://a/b/c/d;p=1/y'], + ['./', bases[2], 'http://a/b/c/d;p=1/'], + ['../', bases[2], 'http://a/b/c/'], + ['../g', bases[2], 'http://a/b/c/g'], + ['../../', bases[2], 'http://a/b/'], + ['../../g' , bases[2], 'http://a/b/g'], + + // http://gbiv.com/protocols/uri/test/rel_examples4.html + // double and triple slash, unknown scheme + ['g:h', bases[3], 'g:h'], + ['g', bases[3], 'fred:///s//a/b/g'], + ['./g', bases[3], 'fred:///s//a/b/g'], + ['g/', bases[3], 'fred:///s//a/b/g/'], + ['/g', bases[3], 'fred:///g'], // may change to fred:///s//a/g + ['//g', bases[3], 'fred://g'], // may change to fred:///s//g + ['//g/x', bases[3], 'fred://g/x'], // may change to fred:///s//g/x + ['///g', bases[3], 'fred:///g'], + ['./', bases[3], 'fred:///s//a/b/'], + ['../', bases[3], 'fred:///s//a/'], + ['../g', bases[3], 'fred:///s//a/g'], + + ['../../', bases[3], 'fred:///s//'], + ['../../g' , bases[3], 'fred:///s//g'], + ['../../../g', bases[3], 'fred:///s/g'], + // may change to fred:///s//a/../../../g + ['../../../../g', bases[3], 'fred:///g'], + + // http://gbiv.com/protocols/uri/test/rel_examples5.html + // double and triple slash, well-known scheme + ['g:h', bases[4], 'g:h'], + ['g', bases[4], 'http:///s//a/b/g'], + ['./g', bases[4], 'http:///s//a/b/g'], + ['g/', bases[4], 'http:///s//a/b/g/'], + ['/g', bases[4], 'http:///g'], // may change to http:///s//a/g + ['//g', bases[4], 'http://g/'], // may change to http:///s//g + ['//g/x', bases[4], 'http://g/x'], // may change to http:///s//g/x + ['///g', bases[4], 'http:///g'], + ['./', bases[4], 'http:///s//a/b/'], + ['../', bases[4], 'http:///s//a/'], + ['../g', bases[4], 'http:///s//a/g'], + ['../../', bases[4], 'http:///s//'], + ['../../g' , bases[4], 'http:///s//g'], + // may change to http:///s//a/../../g + ['../../../g', bases[4], 'http:///s/g'], + // may change to http:///s//a/../../../g + ['../../../../g', bases[4], 'http:///g'], + + // from Dan Connelly's tests in http://www.w3.org/2000/10/swap/uripath.py + ['bar:abc', 'foo:xyz', 'bar:abc'], + ['../abc', 'http://example/x/y/z', 'http://example/x/abc'], + ['http://example/x/abc', 'http://example2/x/y/z', 'http://example/x/abc'], + ['../r', 'http://ex/x/y/z', 'http://ex/x/r'], + ['q/r', 'http://ex/x/y', 'http://ex/x/q/r'], + ['q/r#s', 'http://ex/x/y', 'http://ex/x/q/r#s'], + ['q/r#s/t', 'http://ex/x/y', 'http://ex/x/q/r#s/t'], + ['ftp://ex/x/q/r', 'http://ex/x/y', 'ftp://ex/x/q/r'], + ['', 'http://ex/x/y', 'http://ex/x/y'], + ['', 'http://ex/x/y/', 'http://ex/x/y/'], + ['', 'http://ex/x/y/pdq', 'http://ex/x/y/pdq'], + ['z/', 'http://ex/x/y/', 'http://ex/x/y/z/'], + ['#Animal', + 'file:/swap/test/animal.rdf', + 'file:/swap/test/animal.rdf#Animal'], + ['../abc', 'file:/e/x/y/z', 'file:/e/x/abc'], + ['/example/x/abc', 'file:/example2/x/y/z', 'file:/example/x/abc'], + ['../r', 'file:/ex/x/y/z', 'file:/ex/x/r'], + ['/r', 'file:/ex/x/y/z', 'file:/r'], + ['q/r', 'file:/ex/x/y', 'file:/ex/x/q/r'], + ['q/r#s', 'file:/ex/x/y', 'file:/ex/x/q/r#s'], + ['q/r#', 'file:/ex/x/y', 'file:/ex/x/q/r#'], + ['q/r#s/t', 'file:/ex/x/y', 'file:/ex/x/q/r#s/t'], + ['ftp://ex/x/q/r', 'file:/ex/x/y', 'ftp://ex/x/q/r'], + ['', 'file:/ex/x/y', 'file:/ex/x/y'], + ['', 'file:/ex/x/y/', 'file:/ex/x/y/'], + ['', 'file:/ex/x/y/pdq', 'file:/ex/x/y/pdq'], + ['z/', 'file:/ex/x/y/', 'file:/ex/x/y/z/'], + ['file://meetings.example.com/cal#m1', + 'file:/devel/WWW/2000/10/swap/test/reluri-1.n3', + 'file://meetings.example.com/cal#m1'], + ['file://meetings.example.com/cal#m1', + 'file:/home/connolly/w3ccvs/WWW/2000/10/swap/test/reluri-1.n3', + 'file://meetings.example.com/cal#m1'], + ['./#blort', 'file:/some/dir/foo', 'file:/some/dir/#blort'], + ['./#', 'file:/some/dir/foo', 'file:/some/dir/#'], + // Ryan Lee + ['./', 'http://example/x/abc.efg', 'http://example/x/'], + + + // Graham Klyne's tests + // http://www.ninebynine.org/Software/HaskellUtils/Network/UriTest.xls + // 01-31 are from Connelly's cases + + // 32-49 + ['./q:r', 'http://ex/x/y', 'http://ex/x/q:r'], + ['./p=q:r', 'http://ex/x/y', 'http://ex/x/p=q:r'], + ['?pp/rr', 'http://ex/x/y?pp/qq', 'http://ex/x/y?pp/rr'], + ['y/z', 'http://ex/x/y?pp/qq', 'http://ex/x/y/z'], + ['local/qual@domain.org#frag', + 'mailto:local', + 'mailto:local/qual@domain.org#frag'], + ['more/qual2@domain2.org#frag', + 'mailto:local/qual1@domain1.org', + 'mailto:local/more/qual2@domain2.org#frag'], + ['y?q', 'http://ex/x/y?q', 'http://ex/x/y?q'], + ['/x/y?q', 'http://ex?p', 'http://ex/x/y?q'], + ['c/d', 'foo:a/b', 'foo:a/c/d'], + ['/c/d', 'foo:a/b', 'foo:/c/d'], + ['', 'foo:a/b?c#d', 'foo:a/b?c'], + ['b/c', 'foo:a', 'foo:b/c'], + ['../b/c', 'foo:/a/y/z', 'foo:/a/b/c'], + ['./b/c', 'foo:a', 'foo:b/c'], + ['/./b/c', 'foo:a', 'foo:/b/c'], + ['../../d', 'foo://a//b/c', 'foo://a/d'], + ['.', 'foo:a', 'foo:'], + ['..', 'foo:a', 'foo:'], + + // 50-57[cf. TimBL comments -- + // http://lists.w3.org/Archives/Public/uri/2003Feb/0028.html, + // http://lists.w3.org/Archives/Public/uri/2003Jan/0008.html) + ['abc', 'http://example/x/y%2Fz', 'http://example/x/abc'], + ['../../x%2Fabc', 'http://example/a/x/y/z', 'http://example/a/x%2Fabc'], + ['../x%2Fabc', 'http://example/a/x/y%2Fz', 'http://example/a/x%2Fabc'], + ['abc', 'http://example/x%2Fy/z', 'http://example/x%2Fy/abc'], + ['q%3Ar', 'http://ex/x/y', 'http://ex/x/q%3Ar'], + ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'], + ['/x%2Fabc', 'http://example/x/y/z', 'http://example/x%2Fabc'], + ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'], + + // 70-77 + ['local2@domain2', 'mailto:local1@domain1?query1', 'mailto:local2@domain2'], + ['local2@domain2?query2', + 'mailto:local1@domain1', + 'mailto:local2@domain2?query2'], + ['local2@domain2?query2', + 'mailto:local1@domain1?query1', + 'mailto:local2@domain2?query2'], + ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'], + ['local@domain?query2', 'mailto:?query1', 'mailto:local@domain?query2'], + ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'], + ['http://example/a/b?c/../d', 'foo:bar', 'http://example/a/b?c/../d'], + ['http://example/a/b#c/../d', 'foo:bar', 'http://example/a/b#c/../d'], + + // 82-88 + // @isaacs Disagree. Not how browsers do it. + // ['http:this', 'http://example.org/base/uri', 'http:this'], + // @isaacs Added + ['http:this', 'http://example.org/base/uri', 'http://example.org/base/this'], + ['http:this', 'http:base', 'http:this'], + ['.//g', 'f:/a', 'f://g'], + ['b/c//d/e', 'f://example.org/base/a', 'f://example.org/base/b/c//d/e'], + ['m2@example.ord/c2@example.org', + 'mid:m@example.ord/c@example.org', + 'mid:m@example.ord/m2@example.ord/c2@example.org'], + ['mini1.xml', + 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/', + 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/mini1.xml'], + ['../b/c', 'foo:a/y/z', 'foo:a/b/c'], + + //changeing auth + ['http://diff:auth@www.example.com', + 'http://asdf:qwer@www.example.com', + 'http://diff:auth@www.example.com/'] +]; +relativeTests2.forEach(function(relativeTest) { + var a = url.resolve(relativeTest[1], relativeTest[0]), + e = relativeTest[2]; + assert.equal(a, e, + 'resolve(' + [relativeTest[1], relativeTest[0]] + ') == ' + e + + '\n actual=' + a); +}); + +//if format and parse are inverse operations then +//resolveObject(parse(x), y) == parse(resolve(x, y)) + +//host and hostname are special, in this case a '' value is important +var emptyIsImportant = {'host': true, 'hostname': ''}; + +//format: [from, path, expected] +relativeTests.forEach(function(relativeTest) { + var actual = url.resolveObject(url.parse(relativeTest[0]), relativeTest[1]), + expected = url.parse(relativeTest[2]); + + //because of evaluation order + //resolveObject(parse(x), y) == parse(resolve(x, y)) will differ by + //false-ish values. remove all except host and hostname + for (var i in actual) { + if (actual[i] === undefined || + (!emptyIsImportant.hasOwnProperty(i) && !actual[i])) { + delete actual[i]; + } + } + + assert.deepEqual(actual, expected); + + expected = relativeTest[2]; + actual = url.format(actual); + + assert.equal(actual, expected, + 'format(' + actual + ') == ' + expected + '\nactual:' + actual); +}); + +//format: [to, from, result] +// the test: ['.//g', 'f:/a', 'f://g'] is a fundimental problem +// url.parse('f:/a') does not have a host +// url.resolve('f:/a', './/g') does not have a host becuase you have moved +// down to the g directory. i.e. f: //g, however when this url is parsed +// f:// will indicate that the host is g which is not the case. +// it is unclear to me how to keep this information from being lost +// it may be that a pathname of ////g should colapse to /g but this seems +// to be a lot of work for an edge case. Right now I remove the test +if (relativeTests2[181][0] === './/g' && + relativeTests2[181][1] === 'f:/a' && + relativeTests2[181][2] === 'f://g') { + relativeTests2.splice(181, 1); +} +relativeTests2.forEach(function(relativeTest) { + var actual = url.resolveObject(url.parse(relativeTest[1]), relativeTest[0]), + expected = url.parse(relativeTest[2]); + + //because of evaluation order + //resolveObject(parse(x), y) == parse(resolve(x, y)) will differ by + //false-ish values. remove all except host and hostname + for (var i in actual) { + if (actual[i] === undefined || + (!emptyIsImportant.hasOwnProperty(i) && !actual[i])) { + delete actual[i]; + } + } + + assert.deepEqual(actual, expected); + + var expected = relativeTest[2], + actual = url.format(actual); + + assert.equal(actual, expected, + 'format(' + relativeTest[1] + ') == ' + expected + + '\nactual:' + actual); +}); diff --git a/test/browsertest/nodetests/simple/test-util-format.js b/test/browsertest/nodetests/simple/test-util-format.js new file mode 100644 index 000000000..b5592203a --- /dev/null +++ b/test/browsertest/nodetests/simple/test-util-format.js @@ -0,0 +1,62 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + +var common = require('../common'); +var assert = require('assert'); +var util = require('util'); + +assert.equal(util.format(), ''); +assert.equal(util.format(''), ''); +assert.equal(util.format([]), '[]'); +assert.equal(util.format({}), '{}'); +assert.equal(util.format(null), 'null'); +assert.equal(util.format(true), 'true'); +assert.equal(util.format(false), 'false'); +assert.equal(util.format('test'), 'test'); + +// CHECKME this is for console.log() compatibility - but is it *right*? +assert.equal(util.format('foo', 'bar', 'baz'), 'foo bar baz'); + +assert.equal(util.format('%d', 42.0), '42'); +assert.equal(util.format('%d', 42), '42'); +assert.equal(util.format('%s', 42), '42'); +assert.equal(util.format('%j', 42), '42'); + +assert.equal(util.format('%d', '42.0'), '42'); +assert.equal(util.format('%d', '42'), '42'); +assert.equal(util.format('%s', '42'), '42'); +assert.equal(util.format('%j', '42'), '"42"'); + +assert.equal(util.format('%%s%s', 'foo'), '%sfoo'); + +assert.equal(util.format('%s'), '%s'); +assert.equal(util.format('%s', undefined), 'undefined'); +assert.equal(util.format('%s', 'foo'), 'foo'); +assert.equal(util.format('%s:%s'), '%s:%s'); +assert.equal(util.format('%s:%s', undefined), 'undefined:%s'); +assert.equal(util.format('%s:%s', 'foo'), 'foo:%s'); +assert.equal(util.format('%s:%s', 'foo', 'bar'), 'foo:bar'); +assert.equal(util.format('%s:%s', 'foo', 'bar', 'baz'), 'foo:bar baz'); +assert.equal(util.format('%%%s%%', 'hi'), '%hi%'); +assert.equal(util.format('%%%s%%%%', 'hi'), '%hi%%'); diff --git a/test/browsertest/nodetests/simple/test-util-inspect.js b/test/browsertest/nodetests/simple/test-util-inspect.js new file mode 100644 index 000000000..942e6e6de --- /dev/null +++ b/test/browsertest/nodetests/simple/test-util-inspect.js @@ -0,0 +1,102 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + +var common = require('../common'); +var assert = require('assert'); +var util = require('util'); + +// test the internal isDate implementation +var Date2 = require('vm').runInNewContext('Date'); +var d = new Date2(); +var orig = util.inspect(d); +Date2.prototype.foo = 'bar'; +var after = util.inspect(d); +assert.equal(orig, after); + +// test for sparse array +var a = ['foo', 'bar', 'baz']; +assert.equal(util.inspect(a), '[ \'foo\', \'bar\', \'baz\' ]'); +delete a[1]; +assert.equal(util.inspect(a), '[ \'foo\', , \'baz\' ]'); +assert.equal(util.inspect(a, true), '[ \'foo\', , \'baz\', [length]: 3 ]'); +assert.equal(util.inspect(new Array(5)), '[ , , , , ]'); + +// test for property descriptors +var getter = Object.create(null, { + a: { + get: function() { return 'aaa'; } + } +}); +var setter = Object.create(null, { + b: { + set: function() {} + } +}); +var getterAndSetter = Object.create(null, { + c: { + get: function() { return 'ccc'; }, + set: function() {} + } +}); +assert.equal(util.inspect(getter, true), '{ [a]: [Getter] }'); +assert.equal(util.inspect(setter, true), '{ [b]: [Setter] }'); +assert.equal(util.inspect(getterAndSetter, true), '{ [c]: [Getter/Setter] }'); + +// exceptions should print the error message, not '{}' +assert.equal(util.inspect(new Error()), '[Error]'); +assert.equal(util.inspect(new Error('FAIL')), '[Error: FAIL]'); +assert.equal(util.inspect(new TypeError('FAIL')), '[TypeError: FAIL]'); +assert.equal(util.inspect(new SyntaxError('FAIL')), '[SyntaxError: FAIL]'); +try { + undef(); +} catch (e) { + assert.equal(util.inspect(e), '[ReferenceError: undef is not defined]'); +} +var ex = util.inspect(new Error('FAIL'), true); +assert.ok(ex.indexOf('[Error: FAIL]') != -1); +assert.ok(ex.indexOf('[stack]') != -1); +assert.ok(ex.indexOf('[message]') != -1); +assert.ok(ex.indexOf('[arguments]') != -1); +assert.ok(ex.indexOf('[type]') != -1); + +// GH-1941 +// should not throw: +assert.equal(util.inspect(Object.create(Date.prototype)), '{}'); + +// GH-1944 +assert.doesNotThrow(function() { + var d = new Date(); + d.toUTCString = null; + util.inspect(d); +}); + +assert.doesNotThrow(function() { + var r = /regexp/; + r.toString = null; + util.inspect(r); +}); + +// GH-2225 +var x = { inspect: util.inspect }; +assert.ok(util.inspect(x).indexOf('inspect') != -1); diff --git a/test/browsertest/nodetests/simple/test-util.js b/test/browsertest/nodetests/simple/test-util.js new file mode 100644 index 000000000..87ee77509 --- /dev/null +++ b/test/browsertest/nodetests/simple/test-util.js @@ -0,0 +1,71 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var common = require('../common'); +var assert = require('assert'); +var util = require('util'); +var context = require('vm').runInNewContext; + +// isArray +assert.equal(true, util.isArray([])); +assert.equal(true, util.isArray(Array())); +assert.equal(true, util.isArray(new Array())); +assert.equal(true, util.isArray(new Array(5))); +assert.equal(true, util.isArray(new Array('with', 'some', 'entries'))); +assert.equal(true, util.isArray(context('Array')())); +assert.equal(false, util.isArray({})); +assert.equal(false, util.isArray({ push: function() {} })); +assert.equal(false, util.isArray(/regexp/)); +assert.equal(false, util.isArray(new Error)); +assert.equal(false, util.isArray(Object.create(Array.prototype))); + +// isRegExp +assert.equal(true, util.isRegExp(/regexp/)); +assert.equal(true, util.isRegExp(RegExp())); +assert.equal(true, util.isRegExp(new RegExp())); +assert.equal(true, util.isRegExp(context('RegExp')())); +assert.equal(false, util.isRegExp({})); +assert.equal(false, util.isRegExp([])); +assert.equal(false, util.isRegExp(new Date())); +assert.equal(false, util.isRegExp(Object.create(RegExp.prototype))); + +// isDate +assert.equal(true, util.isDate(new Date())); +assert.equal(true, util.isDate(new Date(0))); +assert.equal(true, util.isDate(new (context('Date')))); +assert.equal(false, util.isDate(Date())); +assert.equal(false, util.isDate({})); +assert.equal(false, util.isDate([])); +assert.equal(false, util.isDate(new Error)); +assert.equal(false, util.isDate(Object.create(Date.prototype))); + +// isError +assert.equal(true, util.isError(new Error)); +assert.equal(true, util.isError(new TypeError)); +assert.equal(true, util.isError(new SyntaxError)); +assert.equal(true, util.isError(new (context('Error')))); +assert.equal(true, util.isError(new (context('TypeError')))); +assert.equal(true, util.isError(new (context('SyntaxError')))); +assert.equal(false, util.isError({})); +assert.equal(false, util.isError({ name: 'Error', message: '' })); +assert.equal(false, util.isError([])); +assert.equal(false, util.isError(Object.create(Error.prototype))); diff --git a/test/browsertest/test.html b/test/browsertest/test.html index 62b3e4f7b..5601052c8 100644 --- a/test/browsertest/test.html +++ b/test/browsertest/test.html @@ -10,15 +10,15 @@ }; window.writing = true; - - - + + +