From 0d54d0c554671aa8e3c7ce6c27973c0d69448c89 Mon Sep 17 00:00:00 2001 From: Mathias Bynens Date: Sat, 3 Aug 2013 07:48:17 +0200 Subject: [PATCH] Remove broken support for regular expressions It may return at some point in the future. Ref. #10 and #11. --- README.md | 17 ++++++++--------- jsesc.js | 41 ++++++++++++++++++++--------------------- src/jsesc.js | 41 ++++++++++++++++++++--------------------- tests/tests.js | 39 ++++++++------------------------------- 4 files changed, 56 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index f1a05dc..ef58cc8 100644 --- a/README.md +++ b/README.md @@ -78,13 +78,9 @@ jsesc('foo 𝌆 bar'); // → 'foo \\uD834\\uDF06 bar' ``` -Instead of a string, the `value` can also be a regular expression, an array, or an object. In such cases, `jsesc` will return a stringified version of the value where any characters that are not printable ASCII symbols are escaped in the same way. +Instead of a string, the `value` can also be an array, or an object. In such cases, `jsesc` will return a stringified version of the value where any characters that are not printable ASCII symbols are escaped in the same way. ```js -// Escaping a regular expression -jsesc(/©𝌆/g); -// → '/\\xA9\\uD834\\uDF06/g' - // Escaping an array jsesc([ 'Ich ♥ Bücher', 'foo 𝌆 bar' @@ -186,8 +182,6 @@ jsesc([ 'Ich ♥ Bücher': 'foo 𝌆 bar' ], { // → '[\'\x49\x63\x68\x20\u2665\x20\x42\xFC\x63\x68\x65\x72\',\'\x66\x6F\x6F\x20\uD834\uDF06\x20\x62\x61\x72\']' ``` -This setting has no effect on the output for regular expressions. Those only use escape sequences for non-printable ASCII symbols and non-ASCII symbols, regardless of the value of the `escapeEverything` setting. - #### `compact` The `compact` option takes a boolean value (`true` or `false`), and defaults to `true` (enabled). When enabled, the output for arrays and objects will be as compact as possible; it won’t be formatted nicely. @@ -235,7 +229,7 @@ jsesc([ 'Ich ♥ Bücher', 'foo 𝌆 bar' ], { // → '[\n \'Ich \u2665 B\xFCcher\',\n\ t\'foo \uD834\uDF06 bar\'\n]' ``` -This setting has no effect on the output for strings or regular expressions. +This setting has no effect on the output for strings. #### `json` @@ -263,9 +257,14 @@ jsesc([ 'foo\x00bar', [1, '©', { 'foo': true, 'qux': null }], 42 ], { 'json': true }); // → '["foo\\u0000bar",[1,"\\u00A9",{"foo":true,"qux":null}],42]' +// Values that aren’t allowed in JSON are run through `JSON.stringify()`: +jsesc([ undefined, -Infinity ], { + 'json': true +}); +// → '[null,null]' ``` -**Note:** Using this option on objects or arrays that contain non-string values relies on `JSON.parse()`. For legacy environments like IE ≤ 7, use [a `JSON` polyfill](http://bestiejs.github.io/json3/). +**Note:** Using this option on objects or arrays that contain non-string values relies on `JSON.stringify()`. For legacy environments like IE ≤ 7, use [a `JSON` polyfill](http://bestiejs.github.io/json3/). ### `jsesc.version` diff --git a/jsesc.js b/jsesc.js index d516170..e802fcf 100644 --- a/jsesc.js +++ b/jsesc.js @@ -1,5 +1,5 @@ /*! http://mths.be/jsesc v0.4.1 by @mathias */ -;(function(root, evil) { +;(function(root) { // Detect free variables `exports` var freeExports = typeof exports == 'object' && exports; @@ -54,9 +54,6 @@ // simple, but good enough for what we need return toString.call(value) == '[object Object]'; }; - var isRegExp = function(value) { - return toString.call(value) == '[object RegExp]'; - }; var isString = function(value) { return typeof value == 'string' || toString.call(value) == '[object String]'; @@ -83,6 +80,19 @@ var regexDigit = /[0-9]/; var regexWhitelist = /[\x20\x21\x23-\x26\x28-\x5B\x5D-\x7E]/; + var stringFromCharCode = String.fromCharCode; + // Modified version of `ucs2encode`; see http://mths.be/punycode + var codePointToSymbol = function(codePoint, strict) { + var output = ''; + if (codePoint > 0xFFFF) { + codePoint -= 0x10000; + output += stringFromCharCode(codePoint >>> 10 & 0x3FF | 0xD800); + codePoint = 0xDC00 | codePoint & 0x3FF; + } + output += stringFromCharCode(codePoint); + return output; + }; + var jsesc = function(argument, options) { // Handle options var defaults = { @@ -91,7 +101,7 @@ 'wrap': false, 'compact': true, 'indent': '\t', - '__indent': '', + '__indent__': '', }; options = extend(defaults, options); if (options.quotes != 'single' && options.quotes != 'double') { @@ -114,9 +124,9 @@ if (isArray(argument)) { result = []; options.wrap = true; - oldIndent = options.__indent; + oldIndent = options.__indent__; indent += oldIndent; - options.__indent = indent; + options.__indent__ = indent; forEach(argument, function(value) { isEmpty = false; result.push( @@ -129,17 +139,6 @@ } return '[' + newLine + result.join(',' + newLine) + newLine + (compact ? '' : oldIndent) + ']'; - } else if (!json && isRegExp(argument)) { - return '/' + jsesc( - evil( - '\'' + argument.source.replace(regexEval, jsesc) + '\'' - ), - extend(options, { - 'wrap': false, - 'escapeEverything': false - } - )) + '/' + (argument.global ? 'g' : '') + - (argument.ignoreCase ? 'i' : '') + (argument.multiline ? 'm' : ''); } else if (!isObject(argument)) { if (json) { // For some values (e.g. `undefined`, `function` objects), @@ -150,9 +149,9 @@ } else { // it’s an object result = []; options.wrap = true; - oldIndent = options.__indent; + oldIndent = options.__indent__; indent += oldIndent; - options.__indent = indent; + options.__indent__ = indent; forOwn(argument, function(key, value) { isEmpty = false; result.push( @@ -244,4 +243,4 @@ root.jsesc = jsesc; } -}(this, eval)); +}(this)); diff --git a/src/jsesc.js b/src/jsesc.js index 92fd94b..722c629 100644 --- a/src/jsesc.js +++ b/src/jsesc.js @@ -1,5 +1,5 @@ /*! http://mths.be/jsesc v<%= version %> by @mathias */ -;(function(root, evil) { +;(function(root) { // Detect free variables `exports` var freeExports = typeof exports == 'object' && exports; @@ -54,9 +54,6 @@ // simple, but good enough for what we need return toString.call(value) == '[object Object]'; }; - var isRegExp = function(value) { - return toString.call(value) == '[object RegExp]'; - }; var isString = function(value) { return typeof value == 'string' || toString.call(value) == '[object String]'; @@ -83,6 +80,19 @@ var regexDigit = /[0-9]/; var regexWhitelist = /<%= whitelist %>/; + var stringFromCharCode = String.fromCharCode; + // Modified version of `ucs2encode`; see http://mths.be/punycode + var codePointToSymbol = function(codePoint, strict) { + var output = ''; + if (codePoint > 0xFFFF) { + codePoint -= 0x10000; + output += stringFromCharCode(codePoint >>> 10 & 0x3FF | 0xD800); + codePoint = 0xDC00 | codePoint & 0x3FF; + } + output += stringFromCharCode(codePoint); + return output; + }; + var jsesc = function(argument, options) { // Handle options var defaults = { @@ -91,7 +101,7 @@ 'wrap': false, 'compact': true, 'indent': '\t', - '__indent': '', + '__indent__': '', }; options = extend(defaults, options); if (options.quotes != 'single' && options.quotes != 'double') { @@ -114,9 +124,9 @@ if (isArray(argument)) { result = []; options.wrap = true; - oldIndent = options.__indent; + oldIndent = options.__indent__; indent += oldIndent; - options.__indent = indent; + options.__indent__ = indent; forEach(argument, function(value) { isEmpty = false; result.push( @@ -129,17 +139,6 @@ } return '[' + newLine + result.join(',' + newLine) + newLine + (compact ? '' : oldIndent) + ']'; - } else if (!json && isRegExp(argument)) { - return '/' + jsesc( - evil( - '\'' + argument.source.replace(regexEval, jsesc) + '\'' - ), - extend(options, { - 'wrap': false, - 'escapeEverything': false - } - )) + '/' + (argument.global ? 'g' : '') + - (argument.ignoreCase ? 'i' : '') + (argument.multiline ? 'm' : ''); } else if (!isObject(argument)) { if (json) { // For some values (e.g. `undefined`, `function` objects), @@ -150,9 +149,9 @@ } else { // it’s an object result = []; options.wrap = true; - oldIndent = options.__indent; + oldIndent = options.__indent__; indent += oldIndent; - options.__indent = indent; + options.__indent__ = indent; forOwn(argument, function(key, value) { isEmpty = false; result.push( @@ -244,4 +243,4 @@ root.jsesc = jsesc; } -}(this, eval)); +}(this)); diff --git a/tests/tests.js b/tests/tests.js index 4ba3559..daff858 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -206,30 +206,6 @@ '["\\u0066\\u006F\\u006F\\u0000\\u0062\\u0061\\u0072\\uFFFD\\u0062\\u0061\\u007A","\\u0066\\u006F\\u006F\\u0000\\u0062\\u0061\\u0072\\uFFFD\\u0062\\u0061\\u007A"]', 'JSON-stringifying a flat array with `escapeEverything: true`' ); - equal( - jsesc(/foo©bar𝌆[a-z0-9]ö/ig), - '/foo\\xA9bar\\uD834\\uDF06[a-z0-9]\\xF6/gi', - 'Escaping a regular expression' - ); - equal( - jsesc(/foo\xA9bar\uD834\uDF06[a-z0-9]\xF6/ig), - '/foo\\xA9bar\\uD834\\uDF06[a-z0-9]\\xF6/gi', - 'Escaping an already-escaped regular expression' - ); - equal( - jsesc(/foo©bar𝌆[a-z0-9]ö/, { - 'escapeEverything': true // should ignore the setting for the regex source part - }), - '/foo\\xA9bar\\uD834\\uDF06[a-z0-9]\\xF6/', - 'Escaping a regular expression with `escapeEverything: true`' - ); - equal( - jsesc(['abc', /foo©bar𝌆[a-z0-9]ö/mig], { - 'escapeEverything': true // should ignore the setting for the regex source part - }), - '[\'\\x61\\x62\\x63\',/foo\\xA9bar\\uD834\\uDF06[a-z0-9]\\xF6/gim]', - 'Escaping an array containing a regular expression with `escapeEverything: true`' - ); }); if (runExtendedTests) { @@ -305,23 +281,24 @@ var testArray = [ undefined, Infinity, new Number(Infinity), -Infinity, new Number(-Infinity), 0, new Number(0), -0, new Number(-0), +0, - new Number(+0), /foo[z-©]/g, new RegExp(/foo[z-©]/g), new Function(), - 'str', function zomg() { return 'desu'; }, null, true, new Boolean(true), - false, new Boolean(false), - { "foo": 42, "baz": /löl/g, "hah": [ 1, 2, 3, { "foo" : 42 } ] } + new Number(+0), new Function(), 'str', + function zomg() { return 'desu'; }, null, true, new Boolean(true), + false, new Boolean(false), { + "foo": 42, "hah": [ 1, 2, 3, { "foo" : 42 } ] + } ]; equal( jsesc(testArray, { 'json': false }), - '[undefined,Infinity,Infinity,-Infinity,-Infinity,0,0,0,0,0,0,/foo[z-\\xA9]/g,/foo[z-\\xA9]/g,function anonymous() {\n\n},\'str\',function zomg() { return \'desu\'; },null,true,true,false,false,{\'foo\':42,\'baz\':/l\\xF6l/g,\'hah\':[1,2,3,{\'foo\':42}]}]', + '[undefined,Infinity,Infinity,-Infinity,-Infinity,0,0,0,0,0,0,function anonymous() {\n\n},\'str\',function zomg() { return \'desu\'; },null,true,true,false,false,{\'foo\':42,\'hah\':[1,2,3,{\'foo\':42}]}]', 'Escaping a non-flat array with all kinds of values' ); equal( jsesc(testArray, { 'json': true }), - '[null,null,null,null,null,0,0,0,0,0,0,{},{},null,"str",null,null,true,true,false,false,{"foo":42,"baz":{},"hah":[1,2,3,{"foo":42}]}]', + '[null,null,null,null,null,0,0,0,0,0,0,null,"str",null,null,true,true,false,false,{"foo":42,"hah":[1,2,3,{"foo":42}]}]', 'Escaping a non-flat array with all kinds of values, with `json: true`' ); equal( @@ -329,7 +306,7 @@ 'json': true, 'compact': false }), - '[\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t{},\n\t{},\n\tnull,\n\t"str",\n\tnull,\n\tnull,\n\ttrue,\n\ttrue,\n\tfalse,\n\tfalse,\n\t{\n\t\t"foo": 42,\n\t\t"baz": {},\n\t\t"hah": [\n\t\t\t1,\n\t\t\t2,\n\t\t\t3,\n\t\t\t{\n\t\t\t\t"foo": 42\n\t\t\t}\n\t\t]\n\t}\n]', + '[\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\tnull,\n\t"str",\n\tnull,\n\tnull,\n\ttrue,\n\ttrue,\n\tfalse,\n\tfalse,\n\t{\n\t\t"foo": 42,\n\t\t"hah": [\n\t\t\t1,\n\t\t\t2,\n\t\t\t3,\n\t\t\t{\n\t\t\t\t"foo": 42\n\t\t\t}\n\t\t]\n\t}\n]', 'Escaping a non-flat array with all kinds of values, with `json: true, compact: false`' ); }