Comparing version 0.2.3 to 1.0.0
264
lib/hjson.js
@@ -11,4 +11,11 @@ /* | ||
hjson_parse(text) | ||
hjson_parse(text, options) | ||
options | ||
{ | ||
keepWsc boolean, keep white space and comments. This is useful | ||
if you want to edit an hjson file and save it while | ||
preserving comments (default false) | ||
} | ||
This method parses Hjson text to produce an object or array. | ||
@@ -21,53 +28,27 @@ It can throw a SyntaxError exception. | ||
hjson_stringify(value, replacer, space) | ||
hjson_stringify(value, options) | ||
obsolete: hjson_stringify(value, replacer, space) | ||
value any JavaScript value, usually an object or array. | ||
value any JavaScript value, usually an object or array. | ||
replacer an optional parameter that determines how object | ||
values are stringified for objects. It can be a | ||
function or an array of strings. | ||
options | ||
{ | ||
keepWsc boolean, keep white space. See parse. | ||
space an optional parameter that specifies the indentation | ||
of nested structures. If it is omitted, the text will | ||
be packed without extra whitespace. If it is a number, | ||
it will specify the number of spaces to indent at each | ||
level. If it is a string (such as '\t' or ' '), | ||
it contains the characters used to indent at each level. | ||
of nested structures. If it is a number, it will specify | ||
the number of spaces to indent at each level. If it is | ||
a string (such as '\t' or ' '), it contains the | ||
characters used to indent at each level. | ||
} | ||
This method produces a Hjson text from a JavaScript value. | ||
replacer do not use / obsolete. | ||
You can provide an optional replacer method. It will be passed the | ||
key and value of each member, with this bound to the containing | ||
object. The value that is returned from your method will be | ||
serialized. If your method returns undefined, then the member will | ||
be excluded from the serialization. | ||
This method produces a Hjson text from a JavaScript value. | ||
If the replacer parameter is an array of strings, then it will be | ||
used to select the members to be serialized. It filters the results | ||
such that only members with keys listed in the replacer array are | ||
stringified. | ||
Values that do not have JSON representations, such as undefined or | ||
functions, will not be serialized. Such values in objects will be | ||
dropped; in arrays they will be replaced with null. | ||
stringify(undefined) returns undefined. | ||
Values that do not have JSON representations, such as undefined or | ||
functions, will not be serialized. Such values in objects will be | ||
dropped; in arrays they will be replaced with null. You can use | ||
a replacer function to replace those with JSON values. | ||
JSON.stringify(undefined) returns undefined. | ||
The optional space parameter produces a stringification of the | ||
value that is filled with line breaks and indentation to make it | ||
easier to read. | ||
If the space parameter is a non-empty string, then that string will | ||
be used for indentation. If the space parameter is a number, then | ||
the indentation will be that many spaces. | ||
Example: | ||
text = JSON.stringify(['e', {pluribus: 'unum'}]); | ||
// text is '["e",{"pluribus":"unum"}]' | ||
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); | ||
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' | ||
The code is based on the the JSON version by Douglas Crockford: | ||
@@ -83,3 +64,3 @@ https://github.com/douglascrockford/JSON-js/blob/master/json2.js | ||
var EOL='\n'; | ||
var hjson_EOL='\n'; | ||
@@ -112,2 +93,3 @@ var hjson_parse = (function () | ||
}; | ||
var keepWsc; // keep whitespace | ||
@@ -333,2 +315,16 @@ // Call error when something is wrong. | ||
var getComment = function (wat) | ||
{ | ||
wat--; | ||
// remove trailing whitespace | ||
for (var i=at-2; i > wat && text[i] <= ' ' && text[i] !== '\n'; i--); | ||
// but only up to EOL | ||
if (text[i] === '\n') i--; | ||
if (text[i] === '\r') i--; | ||
var res = text.substr(wat, i-wat+1); | ||
for (var i=0; i < res.length; i++) | ||
if (res[i] > ' ') return res; | ||
return ""; | ||
} | ||
var array = function () | ||
@@ -339,2 +335,8 @@ { | ||
var array = []; | ||
var kw, wat; | ||
if (keepWsc) | ||
{ | ||
if (Object.defineProperty) Object.defineProperty(array, "__WSC__", { enumerable: false, writable: true }); | ||
array.__WSC__ = kw = []; | ||
} | ||
@@ -344,3 +346,5 @@ if (ch === '[') | ||
next('['); | ||
wat = at; | ||
white(); | ||
if (kw) kw.push(getComment(wat)); | ||
if (ch === ']') | ||
@@ -354,5 +358,7 @@ { | ||
array.push(value()); | ||
wat = at; | ||
white(); | ||
// in Hjson the comma is optional and trailing commas are allowed | ||
if (ch === ',') { next(); white(); } | ||
if (ch === ',') { next(); wat = at; white(); } | ||
if (kw) kw.push(getComment(wat)); | ||
if (ch === ']') | ||
@@ -373,9 +379,18 @@ { | ||
var key, | ||
object = {}; | ||
var key, object = {}; | ||
var kw, wat; | ||
if (keepWsc) | ||
{ | ||
if (Object.defineProperty) Object.defineProperty(object, "__WSC__", { enumerable: false, writable: true }); | ||
object.__WSC__ = kw = { c: {}, o: [] }; | ||
} | ||
function pushWhite(key) { kw.c[key]=getComment(wat); if (key) kw.o.push(key); } | ||
if (ch === '{') | ||
{ | ||
next('{'); | ||
wat = at; | ||
white(); | ||
if (kw) pushWhite(""); | ||
if (ch === '}') | ||
@@ -393,5 +408,7 @@ { | ||
object[key] = value(); | ||
wat = at; | ||
white(); | ||
// in Hjson the comma is optional and trailing commas are allowed | ||
if (ch === ',') { next(); white(); } | ||
if (ch === ',') { next(); wat = at; white(); } | ||
if (kw) pushWhite(key); | ||
if (ch === '}') | ||
@@ -426,6 +443,7 @@ { | ||
return function (source) | ||
return function (source, options) | ||
{ | ||
var result; | ||
keepWsc = options && options.keepWsc; | ||
text = source; | ||
@@ -463,3 +481,3 @@ at = 0; | ||
var indent = ' '; | ||
var rep; | ||
var keepWsc; | ||
@@ -481,3 +499,3 @@ function isWhite(c) { return c <= ' '; } | ||
function quote(string, gap) | ||
function quote(string, gap, hasComment) | ||
{ | ||
@@ -487,3 +505,3 @@ if (!string) return '""'; | ||
needsEscapeExceptBS.lastIndex = 0; | ||
var doEscape = needsEscapeExceptBS.test(string); | ||
var doEscape = hasComment || needsEscapeExceptBS.test(string); | ||
@@ -526,6 +544,6 @@ // Check if we can insert this string without quotes | ||
var res = EOL + gap + "'''"; | ||
var res = hjson_EOL + gap + "'''"; | ||
for (i = 0; i < a.length; i++) | ||
{ | ||
res += EOL + gap + a[i]; | ||
res += hjson_EOL + gap + a[i]; | ||
} | ||
@@ -553,6 +571,19 @@ return res + "'''"; | ||
function str(key, holder) | ||
function str(key, holder, hasComment) | ||
{ | ||
// Produce a string from holder[key]. | ||
function testWsc(str) { return str && str[str[0] === '\r' ? 1 : 0] !== '\n'; } | ||
function wsc(str) | ||
{ | ||
if (!str) return ""; | ||
for (var i = 0; i < str.length; i++) | ||
{ | ||
var c = str[i]; | ||
if (c === '\n' || c === '#') break; | ||
if (c > ' ') return ' # ' + str; | ||
} | ||
return str; | ||
} | ||
var i, // The loop counter. | ||
@@ -563,13 +594,8 @@ k, // The member key. | ||
mind = gap, | ||
eolGap, | ||
partial, | ||
kw, | ||
kwl, | ||
value = holder[key]; | ||
// If we were called with a replacer function, then call the replacer to | ||
// obtain a replacement value. | ||
if (typeof rep === 'function') | ||
{ | ||
value = rep.call(holder, key, value); | ||
} | ||
// What happens next depends on the value's type. | ||
@@ -580,3 +606,3 @@ | ||
case 'string': | ||
return quote(value, gap); | ||
return quote(value, gap, hasComment); | ||
@@ -605,2 +631,3 @@ case 'number': | ||
gap += indent; | ||
eolGap = hjson_EOL + gap; | ||
partial = []; | ||
@@ -615,7 +642,10 @@ | ||
length = value.length; | ||
for (i = 0; i < length; i++) | ||
if (keepWsc) kw = value.__WSC__; | ||
for (i = 0, length = value.length; i < length; i++) | ||
{ | ||
partial[i] = str(i, value) || 'null'; | ||
if (kw) partial.push(wsc(kw[i]) + eolGap); | ||
partial.push(str(i, value, kw ? testWsc(kw[i + 1]) : false) || 'null'); | ||
} | ||
if (kw) partial.push(wsc(kw[i]) + hjson_EOL + mind); | ||
@@ -625,49 +655,51 @@ // Join all of the elements together, separated with newline, and wrap them in | ||
v = partial.length === 0 | ||
? '[]' | ||
: '[' + EOL + gap + partial.join(EOL + gap) + EOL + mind + ']'; | ||
gap = mind; | ||
return v; | ||
if (kw) v = '[' + partial.join('') + ']'; | ||
else if (partial.length === 0) v = '[]'; | ||
else v = '[' + eolGap + partial.join(eolGap) + hjson_EOL + mind + ']'; | ||
} | ||
else | ||
{ | ||
// Otherwise, iterate through all of the keys in the object. | ||
// If the replacer is an array, use it to select the members to be stringified. | ||
if (rep && typeof rep === 'object') | ||
{ | ||
length = rep.length; | ||
for (i = 0; i < length; i++) | ||
if (keepWsc && value.__WSC__) | ||
{ | ||
if (typeof rep[i] === 'string') | ||
kw = value.__WSC__; | ||
kwl = wsc(kw.c[""]); | ||
var keys=kw.o.slice(); | ||
for (k in value) | ||
{ | ||
k = rep[i]; | ||
v = str(k, value); | ||
if (v) | ||
{ | ||
partial.push(quoteName(k) + (gap ? ': ' : ':') + v); | ||
} | ||
if (Object.prototype.hasOwnProperty.call(value, k) && keys.indexOf(k) < 0) | ||
keys.push(k); | ||
} | ||
for (i = 0, length = keys.length; i < length; i++) | ||
{ | ||
k = keys[i]; | ||
partial.push(kwl + eolGap); | ||
kwl = wsc(kw.c[k]); | ||
v = str(k, value, testWsc(kwl)); | ||
if (v) partial.push(quoteName(k) + ': ' + v); | ||
} | ||
partial.push(kwl + hjson_EOL + mind); | ||
} | ||
} | ||
else | ||
{ | ||
// Otherwise, iterate through all of the keys in the object. | ||
for (k in value) | ||
else | ||
{ | ||
if (Object.prototype.hasOwnProperty.call(value, k)) | ||
for (k in value) | ||
{ | ||
v = str(k, value); | ||
if (v) | ||
if (Object.prototype.hasOwnProperty.call(value, k)) | ||
{ | ||
partial.push(quoteName(k) + (gap ? ': ' : ':') + v); | ||
v = str(k, value); | ||
if (v) partial.push(quoteName(k) + ': ' + v); | ||
} | ||
} | ||
} | ||
} | ||
// Join all of the member texts together, separated with newlines, | ||
// and wrap them in braces. | ||
if (kw) v = '{' + partial.join('') + '}'; | ||
else if (partial.length === 0) v = '{}'; | ||
else v = '{' + eolGap + partial.join(eolGap) + hjson_EOL + mind + '}'; | ||
} | ||
// Join all of the member texts together, separated with newlines, | ||
// and wrap them in braces. | ||
v = partial.length === 0 | ||
? '{}' | ||
: '{' + EOL + gap + partial.join(EOL + gap) + EOL + mind + '}'; | ||
gap = mind; | ||
@@ -681,3 +713,3 @@ return v; | ||
return function (value, replacer, space) | ||
return function (value, opt, space) | ||
{ | ||
@@ -692,2 +724,11 @@ // The stringify method takes a value and an optional replacer, and an optional | ||
indent = ' '; | ||
keepWsc = false; | ||
if (typeof opt === 'object') | ||
{ | ||
indent = opt.space || ' '; | ||
keepWsc = opt.keepWsc; | ||
} | ||
// If the space parameter is a number, make an indent string containing that | ||
@@ -704,13 +745,2 @@ // many spaces. If it is a string, it will be used as the indent string. | ||
// If there is a replacer, it must be a function or an array. | ||
// Otherwise, throw an error. | ||
rep = replacer; | ||
if (replacer && typeof replacer !== 'function' && | ||
(typeof replacer !== 'object' || | ||
typeof replacer.length !== 'number')) | ||
{ | ||
throw new Error('hjson_stringify'); | ||
} | ||
// Make a fake root object containing our value under the key of ''. | ||
@@ -728,3 +758,3 @@ // Return the result of stringifying the value. | ||
var os=require('os'); | ||
EOL=os.EOL; | ||
hjson_EOL=os.EOL; | ||
module.exports= | ||
@@ -734,5 +764,5 @@ { | ||
stringify: hjson_stringify, | ||
endOfLine: function() { return EOL; }, | ||
setEndOfLine: function(eol) { EOL=eol; } | ||
endOfLine: function() { return hjson_EOL; }, | ||
setEndOfLine: function(eol) { hjson_EOL=eol; } | ||
}; | ||
} |
@@ -6,3 +6,3 @@ { | ||
"author": "Christian Zangl", | ||
"version": "0.2.3", | ||
"version": "1.0.0", | ||
"tags": [ | ||
@@ -9,0 +9,0 @@ "json", |
# hjson-js | ||
Hjson reference implementation. | ||
Hjson JavaScript reference implementation. | ||
@@ -15,2 +15,8 @@ Hjson is JSON - commas + comments for Humans. | ||
bar: Hello Hjson! | ||
# don't bother with escapes | ||
html: <div class="hello">world</div> | ||
# Hjson is a superset so the normal JSON syntax can be used | ||
"array": [ 1, "two" ] | ||
} | ||
@@ -23,3 +29,5 @@ ``` | ||
"foo": "Hello World!", | ||
"bar": "Hello Hjson!" | ||
"bar": "Hello Hjson!", | ||
"html": "<div class=\"hello\">world</div>", | ||
"array": [ 1, "two" ] | ||
} | ||
@@ -44,5 +52,6 @@ ``` | ||
var text2 = Hjson.stringify(obj); | ||
``` | ||
To keep comments intact see [Editing](#editing-hjson). | ||
## From the Commandline | ||
@@ -66,1 +75,48 @@ | ||
- run `hjson -j test.hjson > test.json` to convert to JSON | ||
# API | ||
See hjson.js | ||
# Editing Hjson | ||
You can modify a Hjson file and keep the whitespace & comments intact. This is useful if an app updates its config file. | ||
``` | ||
// parse, keep whitespace and comments | ||
// (they are stored in a non enumerable __WSC__ member) | ||
var data = Hjson.parse(text, { keepWsc: true }); | ||
// modify like you normally would | ||
data.foo = "text"; | ||
// you can also edit comments by accessing __WSC__ | ||
var wsc = data.__WSC__; | ||
// __WSC__ for objects contains { c: {}, o: [] } | ||
// - c with the actual comment and | ||
// - o (array) with the order of the members | ||
wsc.c.hugo = "just another test"; | ||
wsc.o.splice(2, 0, "hugo"); | ||
data.hugo = "value"; | ||
data.arrayWithComments = [ "a" ]; | ||
// __WSC__ for arrays is just an array with the | ||
// comments ([0] for the space before the elements) | ||
data.arrayWithComments.__WSC__ = [ "before a", "after a" ]; | ||
// convert back to Hjson | ||
console.log(Hjson.stringify(data, { keepWsc: true })); | ||
``` | ||
# Changes | ||
## v1.0.0 | ||
- Switched to v1 for semver | ||
- Adds editing support via the `{ keepWsc: true }` option. | ||
- Removes stringify(value, replacer, space) replacer support | ||
You can still use this syntax but replacer will no longer be called. This was removed in favor of editing support and because replacer provided an incomplete solution. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
25009
632
1
119