Skip to content

Commit 8e80b14

Browse files
committed
Initial commit
0 parents  commit 8e80b14

7 files changed

Lines changed: 483 additions & 0 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.DS_Store
2+
node_modules
3+
coverage

.travis.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
language: node_js
2+
3+
notifications:
4+
email:
5+
on_success: never
6+
on_failure: change
7+
8+
node_js:
9+
- "0.10"

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2013 Blake Embrey (hello@blakeembrey.com)
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# JavaScript Stringify
2+
3+
Stringify is to `eval` as `JSON.stringify` is to `JSON.parse`.
4+
5+
[![Build Status](https://img.shields.io/travis/blakeembrey/javascript-stringify/master.svg)](https://travis-ci.org/blakeembrey/javascript-stringify)
6+
[![NPM version](https://img.shields.io/npm/v/javascript-stringify.svg)](https://www.npmjs.org/package/javascript-stringify)
7+
8+
## Installation
9+
10+
```javascript
11+
npm install javascript-stringify --save
12+
bower install javascript-stringify --save
13+
```
14+
15+
### Node
16+
17+
```javascript
18+
var javascriptStringify = require('javascript-stringify');
19+
```
20+
21+
### AMD
22+
23+
```javascript
24+
define(function (require, exports, module) {
25+
var javascriptStringify = require('javascript-stringify');
26+
});
27+
```
28+
29+
### `<script>` tag
30+
31+
```html
32+
<script src="javascript-stringify.js"></script>
33+
```
34+
35+
## Usage
36+
37+
```javascript
38+
javascriptStringify(value[, replacer [, space]])
39+
```
40+
41+
The API is similar to `JSON.stringify`. However, any value returned by the replacer will be used literally. For this reason, the replacer is passed three arguments - `value`, `indentation` and `stringify`. If you need to continue the stringification process inside your replacer, you can call `stringify` with the updated value.
42+
43+
### Examples
44+
45+
```javascript
46+
javascriptStringify({}); // "{}"
47+
javascriptStringify(true); // "true"
48+
javascriptStringify('foo'); // "'foo'"
49+
50+
javascriptStringify({ x: 5, y: 6}); // "{x:5,y:6}"
51+
javascriptStringify([1, 2, 3, 'string']); // "[1,2,3,'string']"
52+
53+
/**
54+
* Invalid key names are automatically stringified.
55+
*/
56+
57+
javascriptStringify({ 'some-key': 10 }); // "{'some-key':10}"
58+
59+
/**
60+
* Some object types and values can remain identical.
61+
*/
62+
63+
javascriptStringify([/.+/ig, new Number(10), new Date()]); // "[/.+/gi,new Number(10),new Date(1406623295732)]"
64+
65+
/**
66+
* Unknown or circular references are removed.
67+
*/
68+
69+
var obj = { x: 10 };
70+
obj.circular = obj;
71+
72+
javascriptStringify(obj); // "{x:10}"
73+
74+
/**
75+
* Specify indentation - just like `JSON.stringify`.
76+
*/
77+
78+
javascriptStringify({ a: 2 }, null, ' '); // "{\n a: 2\n}"
79+
javascriptStringify({ uno: 1, dos : 2 }, null, '\t'); // "{\n\tuno: 1,\n\tdos: 2\n}"
80+
81+
/**
82+
* Add custom replacer behaviour - like double quoted strings.
83+
*/
84+
85+
javascriptStringify(['test', 'string'], function (value, indent, stringify) {
86+
if (typeof value === 'string') {
87+
return '"' + value.replace(/"/g, '\\"') + '"';
88+
}
89+
90+
return stringify(value);
91+
});
92+
//=> '["test","string"]'
93+
```
94+
95+
## License
96+
97+
MIT

javascript-stringify.js

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
(function (root, stringify) {
2+
/* istanbul ignore else */
3+
if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
4+
// Node.
5+
module.exports = stringify();
6+
} else if (typeof define === 'function' && define.amd) {
7+
// AMD, registers as an anonymous module.
8+
define(function () {
9+
return stringify();
10+
});
11+
} else {
12+
// Browser global.
13+
root.javascriptStringify = stringify();
14+
}
15+
})(this, function () {
16+
/**
17+
* JavaScript reserved word list.
18+
*/
19+
var RESERVED_WORDS = {};
20+
21+
/**
22+
* Map reserved words to the object.
23+
*/
24+
(
25+
'break else new var case finally return void catch for switch while ' +
26+
'continue function this with default if throw delete in try ' +
27+
'do instanceof typeof abstract enum int short boolean export ' +
28+
'interface static byte extends long super char final native synchronized ' +
29+
'class float package throws const goto private transient debugger ' +
30+
'implements protected volatile double import public let yield'
31+
).split(' ').map(function (key) {
32+
RESERVED_WORDS[key] = true;
33+
});
34+
35+
/**
36+
* Check if a variable name is valid.
37+
*
38+
* @param {String} name
39+
* @return {Boolean}
40+
*/
41+
var isValidVariableName = function (name) {
42+
return !RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name);
43+
};
44+
45+
/**
46+
* Return the global variable name.
47+
*
48+
* @return {String}
49+
*/
50+
var getGlobalVariable = function () {
51+
return 'global';
52+
};
53+
54+
/* istanbul ignore next */
55+
if (typeof window === 'object' && typeof window.document === 'object') {
56+
// Support browser environments.
57+
getGlobalVariable = function () {
58+
return 'window';
59+
};
60+
}
61+
62+
/* istanbul ignore next */
63+
if (typeof self === 'object' && typeof self.importScripts === 'function') {
64+
// Support web worker environments.
65+
getGlobalVariable = function () {
66+
return 'self';
67+
};
68+
}
69+
70+
/**
71+
* Convert JavaScript objects into strings.
72+
*
73+
* @type {Object}
74+
*/
75+
var OBJECT_TYPES = {
76+
'[object Array]': function (array, indent, stringify) {
77+
// Map array values to their stringified values with correct indentation.
78+
var values = array.map(function (value) {
79+
return indent + stringify(value).split('\n').join('\n' + indent);
80+
}).join(indent ? ',\n' : ',');
81+
82+
// Wrap the array in newlines if we have indentation set.
83+
if (indent && values) {
84+
return '[\n' + values + '\n]';
85+
}
86+
87+
return '[' + values + ']';
88+
},
89+
'[object Object]': function (object, indent, stringify) {
90+
// Iterate over object keys and concat string together.
91+
var values = Object.keys(object).reduce(function (values, key) {
92+
var value = stringify(object[key]);
93+
94+
// Omit `undefined` object values.
95+
if (value === undefined) {
96+
return values;
97+
}
98+
99+
// String format the key and value data.
100+
key = isValidVariableName(key) ? key : stringify(key);
101+
value = String(value).split('\n').join('\n' + indent);
102+
103+
// Push the current object key and value into the values array.
104+
values.push(indent + key + ':' + (indent ? ' ' : '') + value);
105+
106+
return values;
107+
}, []).join(indent ? ',\n' : ',');
108+
109+
// Wrap the object in newlines if we have indentation set.
110+
if (indent && values) {
111+
return '{\n' + values + '\n}';
112+
}
113+
114+
return '{' + values + '}';
115+
},
116+
'[object Date]': function (date, indent, stringify) {
117+
return 'new Date(' + date.getTime() + ')';
118+
},
119+
'[object String]': function (string, indent, stringify) {
120+
return 'new String(' + stringify(string.toString()) + ')';
121+
},
122+
'[object Number]': function (number, indent, stringify) {
123+
return 'new Number(' + number + ')';
124+
},
125+
'[object Boolean]': function (boolean, indent, stringify) {
126+
return 'new Boolean(' + boolean + ')';
127+
},
128+
'[object RegExp]': String,
129+
'[object Function]': String,
130+
'[object global]': getGlobalVariable,
131+
'[object Window]': getGlobalVariable
132+
};
133+
134+
/**
135+
* Convert JavaScript primitives into strings.
136+
*
137+
* @type {Object}
138+
*/
139+
var PRIMITIVE_TYPES = {
140+
'string': function (string) {
141+
return '\'' + string.replace('\'', '\\\'') + '\'';
142+
},
143+
'number': String,
144+
'object': String,
145+
'boolean': String,
146+
'undefined': String
147+
};
148+
149+
/**
150+
* Convert any value to a string.
151+
*
152+
* @param {*} value
153+
* @param {String} indent
154+
* @param {Function} stringify
155+
* @return {String}
156+
*/
157+
var stringify = function (value, indent, stringify) {
158+
// Convert primitives into strings.
159+
if (Object(value) !== value) {
160+
return PRIMITIVE_TYPES[typeof value](value, indent, stringify);
161+
}
162+
163+
// Use the internal object string to select stringification method.
164+
var toString = OBJECT_TYPES[Object.prototype.toString.call(value)];
165+
166+
// Convert objects into strings.
167+
return toString && toString(value, indent, stringify);
168+
};
169+
170+
/**
171+
* Stringify an object into the literal string.
172+
*
173+
* @param {Object} value
174+
* @param {Function} [replacer]
175+
* @param {(Number|String)} [space]
176+
* @return {String}
177+
*/
178+
return function (value, replacer, space) {
179+
// Convert the spaces into a string.
180+
if (typeof space !== 'string') {
181+
space = new Array(space === +space ? Math.max(0, ++space) : 0).join(' ');
182+
}
183+
184+
/**
185+
* Handle recursion by checking if we've visited this node every iteration.
186+
*
187+
* @param {*} value
188+
* @param {Array} cache
189+
* @return {String}
190+
*/
191+
var recurse = function (value, cache, next) {
192+
// If we've already visited this node before, break the recursion.
193+
if (cache.indexOf(value) > -1) {
194+
return;
195+
}
196+
197+
// Push the value into the values cache to avoid an infinite loop.
198+
cache.push(value);
199+
200+
// Stringify the value and fallback to
201+
return next(value, space, function (value) {
202+
return recurse(value, cache.slice(), next);
203+
});
204+
};
205+
206+
// If the user defined a replacer function, make the recursion function
207+
// a double step process - `replacer -> stringify -> replacer -> etc`.
208+
if (typeof replacer === 'function') {
209+
return recurse(value, [], function (value, space, next) {
210+
return replacer(value, space, function (value) {
211+
return stringify(value, space, next);
212+
});
213+
});
214+
}
215+
216+
return recurse(value, [], stringify);
217+
};
218+
});

package.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "javascript-stringify",
3+
"version": "0.0.0",
4+
"description": "Stringify is to `eval` as `JSON.stringify` is to `JSON.parse`",
5+
"main": "javascript-stringify.js",
6+
"scripts": {
7+
"test": "istanbul cover node_modules/mocha/bin/_mocha -- -R spec"
8+
},
9+
"repository": "https://github.com/blakeembrey/javascript-stringify.git",
10+
"keywords": [
11+
"stringify",
12+
"javascript",
13+
"object",
14+
"string"
15+
],
16+
"author": "Blake Embrey <hello@blakeembrey.com> (http://blakeembrey.me/)",
17+
"license": "MIT",
18+
"readmeFilename": "README.md",
19+
"devDependencies": {
20+
"chai": "^1.9.1",
21+
"istanbul": "^0.3.0",
22+
"mocha": "^1.21.3"
23+
}
24+
}

0 commit comments

Comments
 (0)