26 Commits

Author SHA1 Message Date
Andrey Gubanov
67f22e8ac3 1.7.6 2020-09-16 14:20:49 +03:00
Andrey Gubanov
fcab8c4415 Merge pull request #3 from jpalardy/master
Report dougJSONParse errors in same format as other parse errors
2020-09-16 14:14:23 +03:00
Jonathan Palardy
b4cf02bd80 Report dougJSONParse errors in same format as other parse errors 2020-09-10 20:44:35 -07:00
Andrey Gubanov
d506cf924b Store deprecated package nomnom locally 2019-07-26 17:48:41 +03:00
Andrey Gubanov
d9057c2d39 Minor enhancements 2019-02-10 13:47:10 +02:00
Andrey Gubanov
ac1eae7734 Merge pull request #2 from kdzwinel/patch-1
Remove console.error call
2018-05-27 00:26:25 +03:00
Konrad Dzwinel
3e7c654694 Remove console.error call
Get rid of unwanted console output.
2018-04-30 11:41:48 +02:00
Andrey Gubanov
5abb74c63d 1.7.2 2017-06-05 19:59:02 +03:00
Andrey Gubanov
1c92b6312b Prepublish bundle 2017-06-05 19:58:51 +03:00
Andrey Gubanov
77167762e5 1.7.1 2017-06-05 19:55:51 +03:00
Andrey Gubanov
094ff0d03d Throw error when Doug parser throws 'Unexpected' 2017-06-05 19:55:27 +03:00
Andrey Gubanov (his digital copy)
a4ff31cbf7 modify readme and publish 1.7.0 2016-09-25 16:22:26 +03:00
Andrey Gubanov (his digital copy)
091450f3a2 bug fixes 2016-09-25 16:07:40 +03:00
Andrey Gubanov (his digital copy)
1d18f80902 bundle 2016-09-25 15:55:43 +03:00
Andrey Gubanov (his digital copy)
39f5c3dc89 add Doug's json parse and check for bad empty symbols and duplicates 2016-09-25 15:29:03 +03:00
Andrey Gubanov (his digital copy)
042f6b9507 add Doug's json parse and check for bad empty symbols and duplicates 2016-09-25 15:27:04 +03:00
Zachary Carter
f5cb6e7e98 1.6.2 2014-08-12 12:34:37 -07:00
Zachary Carter
b9a537a3c1 pretty print should exit with error code when invalid
Fixes issue #38.
2014-08-12 12:33:53 -07:00
Zachary Carter
5834ccff24 1.6.1 2014-08-12 12:03:37 -07:00
Zach Carter
957bf60ff3 Merge pull request #49 from diurnalist/feature/sort-arrays
Fix issue where objects nested in arrays would not have sorted keys
2014-08-12 12:01:22 -07:00
Jason Anderson
2b833324ba Fix issue where objects nested in arrays would not have sorted keys 2014-07-17 14:49:26 +02:00
Zach Carter
8cd6a4f3d2 Merge pull request #33 from louismrose/master
Add bower.json.
2013-08-08 23:26:30 -07:00
Louis Rose
271d8982d0 Add bower.json. 2013-08-06 14:07:52 +01:00
Zach Carter
57edf0b901 Merge pull request #31 from tmcw/master
Makefile should use local binaries, not global
2013-07-24 08:43:24 -07:00
Tom MacWright
a6a6ff142d Makefile should use local binaries, not global 2013-07-24 10:13:42 -04:00
Zachary Carter
09c10ae1ac Update readme with latest command-line arguments 2013-04-02 10:47:25 -07:00
14 changed files with 2197 additions and 40 deletions

View File

@@ -2,9 +2,9 @@
all: build test site
build:
jison src/jsonlint.y src/jsonlint.l
./node_modules/jison/lib/cli.js src/jsonlint.y src/jsonlint.l
mv jsonlint.js lib/jsonlint.js
node scripts/bundle.js | uglifyjs > web/jsonlint.js
node scripts/bundle.js | ./node_modules/uglify-js/bin/uglifyjs > web/jsonlint.js
site:
cp web/jsonlint.js ../jsonlint-pages/jsonlint.js

View File

@@ -1,12 +1,18 @@
JSON Lint
JSON Lint [![npm version](https://badge.fury.io/js/jsonlint-mod.svg)](https://badge.fury.io/js/jsonlint-mod)
=========
A pure [JavaScript version](http://zaach.github.com/jsonlint/) of the service provided at [jsonlint.com](http://jsonlint.com).
The fork is a modified version of [jsonlint](http://zaach.github.com/jsonlint/) enhanced by the original Douglas Crockford's JSON parser which brought the following features:
- Handle hidden chars
- Handle key duplicates
- Web version exported as CJS module to use with Webpack and other bundlers
A modified description below.
## Command line interface
Install jsonlint with npm to use the command line interface:
npm install jsonlint -g
npm install jsonlint-mod -g
Validate a file like so:
@@ -22,35 +28,34 @@ jsonlint will either report a syntax error with details or pretty print the sour
$ jsonlint -h
usage: jsonlint <file> [options]
Usage: jsonlint [file] [options]
file file to parse; otherwise uses stdin
options:
Options:
-v, --version print version and exit
-s, --sort-keys sort object keys
-i, --in-place overwrite the file
-t CHAR, --indent CHAR character(s) to use for indentation
-t CHAR, --indent CHAR character(s) to use for indentation [ ]
-c, --compact compact error display
-V, --validate a JSON schema to use for validation
-e, --environment which specification of JSON Schema the validation file uses
-q, --quiet do not print the parsed json to STDOUT
-e, --environment which specification of JSON Schema the validation file uses [json-schema-draft-03]
-q, --quiet do not print the parsed json to STDOUT [false]
-p, --pretty-print force pretty printing even if invalid
## Module interface
I'm not sure why you wouldn't use the built in `JSON.parse` but you can use jsonlint from a CommonJS module:
var jsonlint = require("jsonlint-mod");
var jsonlint = require("jsonlint");
jsonlint.parse('{"creative?": false}');
jsonlint.parse('{"creative?": false}');
It returns the parsed object or throws an `Error`.
## Vim Plugins
* [Syntastic](http://www.vim.org/scripts/script.php?script_id=2736)
* [sourcebeautify](http://www.vim.org/scripts/script.php?script_id=4079)
* [sourcebeautify](http://www.vim.org/scripts/script.php?script_id=4079)
## MIT License
@@ -60,4 +65,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
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.
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.

16
bower.json Normal file
View File

@@ -0,0 +1,16 @@
{
"name": "jsonlint",
"version": "1.6.0",
"main": "lib/jsonlint.js",
"ignore": [
"**/.*",
"node_modules",
"scripts",
"src",
"test",
"web",
"package.json",
"Makefile",
"README.md"
]
}

View File

@@ -6,7 +6,7 @@ var parser = require("./jsonlint").parser;
var JSV = require("JSV").JSV;
var formatter = require("./formatter.js").formatter;
var options = require("nomnom")
var options = require("./nomnom")
.script("jsonlint")
.options({
file: {
@@ -67,7 +67,12 @@ var options = require("nomnom")
if (options.compact) {
var fileName = options.file? options.file + ': ' : '';
parser.parseError = parser.lexer.parseError = function(str, hash) {
console.error(fileName + 'line '+ hash.loc.first_line +', col '+ hash.loc.last_column +', found: \''+ hash.token +'\' - expected: '+ hash.expected.join(', ') +'.');
// error from dougJSONParse
if (hash.message) {
console.error(fileName + 'line '+ hash.line +', col '+ hash.col +',', hash.message + '.');
} else {
console.error(fileName + 'line '+ hash.loc.first_line +', col '+ hash.loc.last_column +', found: \''+ hash.token +'\' - expected: '+ hash.expected.join(', ') +'.');
}
throw new Error(str);
};
}
@@ -90,9 +95,9 @@ function parse (source) {
}
}
return JSON.stringify(parsed,null,options.indent);
return JSON.stringify(parsed, null, options.indent);
} catch (e) {
if ( options.forcePrettyPrint ) {
if (options.forcePrettyPrint) {
/* From https://github.com/umbrae/jsonlintdotcom:
* If we failed to validate, run our manual formatter and then re-validate so that we
* can get a better line number. On a successful validate, we don't want to run our
@@ -107,16 +112,15 @@ function parse (source) {
if (! options.compact) {
console.error(e);
}
return formatted;
process.exit(1);
// force the pretty print before exiting
console.log(formatted);
}
} else {
if (! options.compact) {
console.error(e);
}
process.exit(1);
}
process.exit(1);
}
}
@@ -154,7 +158,9 @@ function main (args) {
// from http://stackoverflow.com/questions/1359761/sorting-a-json-object-in-javascript
function sortObject(o) {
if (Object.prototype.toString.call(o) !== '[object Object]') {
if (Array.isArray(o)) {
return o.map(sortObject);
} else if (Object.prototype.toString.call(o) !== '[object Object]') {
return o;
}

360
lib/doug-json-parse.js Normal file
View File

@@ -0,0 +1,360 @@
/*
json_parse.js
2016-05-02
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
This file creates a json_parse function.
json_parse(text, reviver)
This method parses a JSON text to produce an object or array.
It can throw a SyntaxError exception.
The optional reviver parameter is a function that can filter and
transform the results. It receives each of the keys and values,
and its return value is used instead of the original value.
If it returns what it received, then the structure is not modified.
If it returns undefined then the member is deleted.
Example:
// Parse the text. Values that look like ISO date strings will
// be converted to Date objects.
myData = json_parse(text, function (key, value) {
var a;
if (typeof value === "string") {
a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+a[5], +a[6]));
}
}
return value;
});
This is a reference implementation. You are free to copy, modify, or
redistribute.
This code should be minified before deployment.
See http://javascript.crockford.com/jsmin.html
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
NOT CONTROL.
*/
/*jslint for */
/*property
at, b, call, charAt, f, fromCharCode, hasOwnProperty, message, n, name,
prototype, push, r, t, text
*/
var ___dougJSONParse = (function () {
"use strict";
// This is a function that can parse a JSON text, producing a JavaScript
// data structure. It is a simple, recursive descent parser. It does not use
// eval or regular expressions, so it can be used as a model for implementing
// a JSON parser in other languages.
// We are defining the function inside of another function to avoid creating
// global variables.
var at; // The index of the current character
var ch; // The current character
var escapee = {
"\"": "\"",
"\\": "\\",
"/": "/",
b: "\b",
f: "\f",
n: "\n",
r: "\r",
t: "\t"
};
var text;
var error = function (m) {
// Call error when something is wrong.
throw {
name: "SyntaxError",
message: m,
at: at,
text: text
};
};
var next = function (c) {
// If a c parameter is provided, verify that it matches the current character.
if (c && c !== ch) {
error("Expected '" + c + "' instead of '" + ch + "'");
}
// Get the next character. When there are no more characters,
// return the empty string.
ch = text.charAt(at);
at += 1;
return ch;
};
var number = function () {
// Parse a number value.
var value;
var string = "";
if (ch === "-") {
string = "-";
next("-");
}
while (ch >= "0" && ch <= "9") {
string += ch;
next();
}
if (ch === ".") {
string += ".";
while (next() && ch >= "0" && ch <= "9") {
string += ch;
}
}
if (ch === "e" || ch === "E") {
string += ch;
next();
if (ch === "-" || ch === "+") {
string += ch;
next();
}
while (ch >= "0" && ch <= "9") {
string += ch;
next();
}
}
value = +string;
if (!isFinite(value)) {
error("Bad number");
} else {
return value;
}
};
var string = function () {
// Parse a string value.
var hex;
var i;
var value = "";
var uffff;
// When parsing for string values, we must look for " and \ characters.
if (ch === "\"") {
while (next()) {
if (ch === "\"") {
next();
return value;
}
if (ch === "\\") {
next();
if (ch === "u") {
uffff = 0;
for (i = 0; i < 4; i += 1) {
hex = parseInt(next(), 16);
if (!isFinite(hex)) {
break;
}
uffff = uffff * 16 + hex;
}
value += String.fromCharCode(uffff);
} else if (typeof escapee[ch] === "string") {
value += escapee[ch];
} else {
break;
}
} else {
value += ch;
}
}
}
error("Bad string");
};
var white = function () {
// Skip whitespace.
while (ch && ch <= " ") {
next();
}
};
var word = function () {
// true, false, or null.
switch (ch) {
case "t":
next("t");
next("r");
next("u");
next("e");
return true;
case "f":
next("f");
next("a");
next("l");
next("s");
next("e");
return false;
case "n":
next("n");
next("u");
next("l");
next("l");
return null;
}
error("Unexpected '" + ch + "'");
};
var value; // Place holder for the value function.
var array = function () {
// Parse an array value.
var arr = [];
if (ch === "[") {
next("[");
white();
if (ch === "]") {
next("]");
return arr; // empty array
}
while (ch) {
arr.push(value());
white();
if (ch === "]") {
next("]");
return arr;
}
next(",");
white();
}
}
error("Bad array");
};
var object = function () {
// Parse an object value.
var key;
var obj = {};
if (ch === "{") {
next("{");
white();
if (ch === "}") {
next("}");
return obj; // empty object
}
while (ch) {
key = string();
white();
next(":");
if (Object.hasOwnProperty.call(obj, key)) {
error("Duplicate key '" + key + "'");
}
obj[key] = value();
white();
if (ch === "}") {
next("}");
return obj;
}
next(",");
white();
}
}
error("Bad object");
};
value = function () {
// Parse a JSON value. It could be an object, an array, a string, a number,
// or a word.
white();
switch (ch) {
case "{":
return object();
case "[":
return array();
case "\"":
return string();
case "-":
return number();
default:
return (ch >= "0" && ch <= "9")
? number()
: word();
}
};
// Return the json_parse function. It will have access to all of the above
// functions and variables.
return function (source, reviver) {
var result;
text = source;
at = 0;
ch = " ";
result = value();
white();
if (ch) {
error("Syntax error");
}
// If there is a reviver function, we recursively walk the new structure,
// passing each name/value pair to the reviver function for possible
// transformation, starting with a temporary root object that holds the result
// in an empty key. If there is not a reviver function, we simply return the
// result.
return (typeof reviver === "function")
? (function walk(holder, key) {
var k;
var v;
var val = holder[key];
if (val && typeof val === "object") {
for (k in val) {
if (Object.prototype.hasOwnProperty.call(val, k)) {
v = walk(val, k);
if (v !== undefined) {
val[k] = v;
} else {
delete val[k];
}
}
}
}
return reviver.call(holder, key, val);
}({"": result}, ""))
: result;
};
}());
if(typeof module === 'object' && module.exports) {
module.exports = ___dougJSONParse;
}

View File

@@ -17,7 +17,7 @@ case 1: // replace escaped characters with actual character
.replace(/\\v/g,'\v')
.replace(/\\f/g,'\f')
.replace(/\\b/g,'\b');
break;
case 2:this.$ = Number(yytext);
break;
@@ -341,7 +341,7 @@ next:function () {
if (this._input === "") {
return this.EOF;
} else {
this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
{text: "", token: null, line: this.yylineno});
}
},
@@ -412,6 +412,28 @@ return lexer;})()
parser.lexer = lexer;
return parser;
})();
var origParse = jsonlint.parse;
jsonlint.parse = function(input) {
var result = origParse.call(jsonlint, input);
var dougJSONParse = typeof ___dougJSONParse === 'undefined' ? require('./doug-json-parse') : ___dougJSONParse;
try {
dougJSONParse(input);
} catch(e) {
if(/Duplicate key|Bad string|Unexpected/.test(e.message)) {
var linesUntilError = input.substring(0, e.at).split('\n');
var line = linesUntilError.length;
var col = linesUntilError[line - 1].length - 1;
this.parseError(e.message, {line: line, col: col, message: e.message.replace(/./, function(l) { return l.toLowerCase(); })});
throw SyntaxError(e.message + ' on line ' + line);
}
}
return result;
}
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
exports.parser = jsonlint;
exports.parse = function () { return jsonlint.parse.apply(jsonlint, arguments); }
@@ -429,4 +451,4 @@ exports.main = function commonjsMain(args) {
if (typeof module !== 'undefined' && require.main === module) {
exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
}
}
}

611
lib/nomnom.js Normal file
View File

@@ -0,0 +1,611 @@
/*
nomnom is an option parser for node. It noms your args and gives them back to you in a hash.
https://github.com/harthur/nomnom
Copyright (c) 2010 Heather Arthur
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 _ = require("underscore"), chalk = require('chalk');
function ArgParser() {
this.commands = {}; // expected commands
this.specs = {}; // option specifications
}
ArgParser.prototype = {
/* Add a command to the expected commands */
command : function(name) {
var command;
if (name) {
command = this.commands[name] = {
name: name,
specs: {}
};
}
else {
command = this.fallback = {
specs: {}
};
}
// facilitates command('name').options().cb().help()
var chain = {
options : function(specs) {
command.specs = specs;
return chain;
},
opts : function(specs) {
// old API
return this.options(specs);
},
option : function(name, spec) {
command.specs[name] = spec;
return chain;
},
callback : function(cb) {
command.cb = cb;
return chain;
},
help : function(help) {
command.help = help;
return chain;
},
usage : function(usage) {
command._usage = usage;
return chain;
}
};
return chain;
},
nocommand : function() {
return this.command();
},
options : function(specs) {
this.specs = specs;
return this;
},
opts : function(specs) {
// old API
return this.options(specs);
},
globalOpts : function(specs) {
// old API
return this.options(specs);
},
option : function(name, spec) {
this.specs[name] = spec;
return this;
},
usage : function(usage) {
this._usage = usage;
return this;
},
printer : function(print) {
this.print = print;
return this;
},
script : function(script) {
this._script = script;
return this;
},
scriptName : function(script) {
// old API
return this.script(script);
},
help : function(help) {
this._help = help;
return this;
},
colors: function() {
// deprecated - colors are on by default now
return this;
},
nocolors : function() {
this._nocolors = true;
return this;
},
parseArgs : function(argv) {
// old API
return this.parse(argv);
},
nom : function(argv) {
return this.parse(argv);
},
parse : function(argv) {
this.print = this.print || function(str, code) {
console.log(str);
process.exit(code || 0);
};
this._help = this._help || "";
this._script = this._script || process.argv[0] + " "
+ require('path').basename(process.argv[1]);
this.specs = this.specs || {};
var argv = argv || process.argv.slice(2);
var arg = Arg(argv[0]).isValue && argv[0],
command = arg && this.commands[arg],
commandExpected = !_(this.commands).isEmpty();
if (commandExpected) {
if (command) {
_(this.specs).extend(command.specs);
this._script += " " + command.name;
if (command.help) {
this._help = command.help;
}
this.command = command;
}
else if (arg) {
return this.print(this._script + ": no such command '" + arg + "'", 1);
}
else {
// no command but command expected e.g. 'git -v'
var helpStringBuilder = {
list : function() {
return 'one of: ' + _(this.commands).keys().join(", ");
},
twoColumn : function() {
// find the longest command name to ensure horizontal alignment
var maxLength = _(this.commands).max(function (cmd) {
return cmd.name.length;
}).name.length;
// create the two column text strings
var cmdHelp = _.map(this.commands, function(cmd, name) {
var diff = maxLength - name.length;
var pad = new Array(diff + 4).join(" ");
return " " + [ name, pad, cmd.help ].join(" ");
});
return "\n" + cmdHelp.join("\n");
}
};
// if there are a small number of commands and all have help strings,
// display them in a two column table; otherwise use the brief version.
// The arbitrary choice of "20" comes from the number commands git
// displays as "common commands"
var helpType = 'list';
if (_(this.commands).size() <= 20) {
if (_(this.commands).every(function (cmd) { return cmd.help; })) {
helpType = 'twoColumn';
}
}
this.specs.command = {
position: 0,
help: helpStringBuilder[helpType].call(this)
}
if (this.fallback) {
_(this.specs).extend(this.fallback.specs);
this._help = this.fallback.help;
} else {
this.specs.command.required = true;
}
}
}
if (this.specs.length === undefined) {
// specs is a hash not an array
this.specs = _(this.specs).map(function(opt, name) {
opt.name = name;
return opt;
});
}
this.specs = this.specs.map(function(opt) {
return Opt(opt);
});
if (argv.indexOf("--help") >= 0 || argv.indexOf("-h") >= 0) {
return this.print(this.getUsage());
}
var options = {};
var args = argv.map(function(arg) {
return Arg(arg);
})
.concat(Arg());
var positionals = [];
/* parse the args */
var that = this;
args.reduce(function(arg, val) {
/* positional */
if (arg.isValue) {
positionals.push(arg.value);
}
else if (arg.chars) {
var last = arg.chars.pop();
/* -cfv */
(arg.chars).forEach(function(ch) {
that.setOption(options, ch, true);
});
/* -v key */
if (!that.opt(last).flag) {
if (val.isValue) {
that.setOption(options, last, val.value);
return Arg(); // skip next turn - swallow arg
}
else {
that.print("'-" + (that.opt(last).name || last) + "'"
+ " expects a value\n\n" + that.getUsage(), 1);
}
}
else {
/* -v */
that.setOption(options, last, true);
}
}
else if (arg.full) {
var value = arg.value;
/* --key */
if (value === undefined) {
/* --key value */
if (!that.opt(arg.full).flag) {
if (val.isValue) {
that.setOption(options, arg.full, val.value);
return Arg();
}
else {
that.print("'--" + (that.opt(arg.full).name || arg.full) + "'"
+ " expects a value\n\n" + that.getUsage(), 1);
}
}
else {
/* --flag */
value = true;
}
}
that.setOption(options, arg.full, value);
}
return val;
});
positionals.forEach(function(pos, index) {
this.setOption(options, index, pos);
}, this);
options._ = positionals;
this.specs.forEach(function(opt) {
if (opt.default !== undefined && options[opt.name] === undefined) {
options[opt.name] = opt.default;
}
}, this);
// exit if required arg isn't present
this.specs.forEach(function(opt) {
if (opt.required && options[opt.name] === undefined) {
var msg = opt.name + " argument is required";
msg = this._nocolors ? msg : chalk.red(msg);
this.print("\n" + msg + "\n" + this.getUsage(), 1);
}
}, this);
if (command && command.cb) {
command.cb(options);
}
else if (this.fallback && this.fallback.cb) {
this.fallback.cb(options);
}
return options;
},
getUsage : function() {
if (this.command && this.command._usage) {
return this.command._usage;
}
else if (this.fallback && this.fallback._usage) {
return this.fallback._usage;
}
if (this._usage) {
return this._usage;
}
// todo: use a template
var str = "\n"
if (!this._nocolors) {
str += chalk.bold("Usage:");
}
else {
str += "Usage:";
}
str += " " + this._script;
var positionals = _(this.specs).select(function(opt) {
return opt.position != undefined;
})
positionals = _(positionals).sortBy(function(opt) {
return opt.position;
});
var options = _(this.specs).select(function(opt) {
return opt.position === undefined;
});
// assume there are no gaps in the specified pos. args
positionals.forEach(function(pos) {
str += " ";
var posStr = pos.string;
if (!posStr) {
posStr = pos.name || "arg" + pos.position;
if (pos.required) {
posStr = "<" + posStr + ">";
} else {
posStr = "[" + posStr + "]";
}
if (pos.list) {
posStr += "...";
}
}
str += posStr;
});
if (options.length) {
if (!this._nocolors) {
// must be a better way to do this
str += chalk.blue(" [options]");
}
else {
str += " [options]";
}
}
if (options.length || positionals.length) {
str += "\n\n";
}
function spaces(length) {
var spaces = "";
for (var i = 0; i < length; i++) {
spaces += " ";
}
return spaces;
}
var longest = positionals.reduce(function(max, pos) {
return pos.name.length > max ? pos.name.length : max;
}, 0);
positionals.forEach(function(pos) {
var posStr = pos.string || pos.name;
str += posStr + spaces(longest - posStr.length) + " ";
if (!this._nocolors) {
str += chalk.grey(pos.help || "")
}
else {
str += (pos.help || "")
}
str += "\n";
}, this);
if (positionals.length && options.length) {
str += "\n";
}
if (options.length) {
if (!this._nocolors) {
str += chalk.blue("Options:");
}
else {
str += "Options:";
}
str += "\n"
var longest = options.reduce(function(max, opt) {
return opt.string.length > max && !opt.hidden ? opt.string.length : max;
}, 0);
options.forEach(function(opt) {
if (!opt.hidden) {
str += " " + opt.string + spaces(longest - opt.string.length) + " ";
var defaults = (opt.default != null ? " [" + opt.default + "]" : "");
var help = opt.help ? opt.help + defaults : "";
str += this._nocolors ? help: chalk.grey(help);
str += "\n";
}
}, this);
}
if (this._help) {
str += "\n" + this._help;
}
return str;
}
};
ArgParser.prototype.opt = function(arg) {
// get the specified opt for this parsed arg
var match = Opt({});
this.specs.forEach(function(opt) {
if (opt.matches(arg)) {
match = opt;
}
});
return match;
};
ArgParser.prototype.setOption = function(options, arg, value) {
var option = this.opt(arg);
if (option.callback) {
var message = option.callback(value);
if (typeof message == "string") {
this.print(message, 1);
}
}
if (option.type != "string") {
try {
// infer type by JSON parsing the string
value = JSON.parse(value)
}
catch(e) {}
}
if (option.transform) {
value = option.transform(value);
}
var name = option.name || arg;
if (option.choices && option.choices.indexOf(value) == -1) {
this.print(name + " must be one of: " + option.choices.join(", "), 1);
}
if (option.list) {
if (!options[name]) {
options[name] = [value];
}
else {
options[name].push(value);
}
}
else {
options[name] = value;
}
};
/* an arg is an item that's actually parsed from the command line
e.g. "-l", "log.txt", or "--logfile=log.txt" */
var Arg = function(str) {
var abbrRegex = /^\-(\w+?)$/,
fullRegex = /^\-\-(no\-)?(.+?)(?:=(.+))?$/,
valRegex = /^[^\-].*/;
var charMatch = abbrRegex.exec(str),
chars = charMatch && charMatch[1].split("");
var fullMatch = fullRegex.exec(str),
full = fullMatch && fullMatch[2];
var isValue = str !== undefined && (str === "" || valRegex.test(str));
var value;
if (isValue) {
value = str;
}
else if (full) {
value = fullMatch[1] ? false : fullMatch[3];
}
return {
str: str,
chars: chars,
full: full,
value: value,
isValue: isValue
}
}
/* an opt is what's specified by the user in opts hash */
var Opt = function(opt) {
var strings = (opt.string || "").split(","),
abbr, full, metavar;
for (var i = 0; i < strings.length; i++) {
var string = strings[i].trim(),
matches;
if (matches = string.match(/^\-([^-])(?:\s+(.*))?$/)) {
abbr = matches[1];
metavar = matches[2];
}
else if (matches = string.match(/^\-\-(.+?)(?:[=\s]+(.+))?$/)) {
full = matches[1];
metavar = metavar || matches[2];
}
}
matches = matches || [];
var abbr = opt.abbr || abbr, // e.g. v from -v
full = opt.full || full, // e.g. verbose from --verbose
metavar = opt.metavar || metavar; // e.g. PATH from '--config=PATH'
var string;
if (opt.string) {
string = opt.string;
}
else if (opt.position === undefined) {
string = "";
if (abbr) {
string += "-" + abbr;
if (metavar)
string += " " + metavar
string += ", ";
}
string += "--" + (full || opt.name);
if (metavar) {
string += " " + metavar;
}
}
opt = _(opt).extend({
name: opt.name || full || abbr,
string: string,
abbr: abbr,
full: full,
metavar: metavar,
matches: function(arg) {
return opt.full == arg || opt.abbr == arg || opt.position == arg
|| opt.name == arg || (opt.list && arg >= opt.position);
}
});
return opt;
}
var createParser = function() {
return new ArgParser();
}
var nomnom = createParser();
for (var i in nomnom) {
if (typeof nomnom[i] == "function") {
createParser[i] = _(nomnom[i]).bind(nomnom);
}
}
module.exports = createParser;

240
package-lock.json generated Normal file
View File

@@ -0,0 +1,240 @@
{
"name": "jsonlint-mod",
"version": "1.7.6",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"JSONSelect": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.4.0.tgz",
"integrity": "sha1-oI7cxn6z/L6Z7WMIVTRKDPKCu40=",
"dev": true
},
"JSV": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
"integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c="
},
"amdefine": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
"dev": true,
"optional": true
},
"ansi-font": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/ansi-font/-/ansi-font-0.0.2.tgz",
"integrity": "sha1-iQMBvVhBRi/TnAt3Ca/R9SUUMzE=",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"cjson": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/cjson/-/cjson-0.3.0.tgz",
"integrity": "sha1-5kObkHA9MS/24iJAl76pLOPQKhQ=",
"dev": true,
"requires": {
"jsonlint": "1.6.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"colors": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz",
"integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=",
"dev": true
},
"commander": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
"dev": true
},
"ebnf-parser": {
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/ebnf-parser/-/ebnf-parser-0.1.10.tgz",
"integrity": "sha1-zR9rpHfFY4xAyX7ZtXLbW6tdgzE=",
"dev": true
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"escodegen": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz",
"integrity": "sha1-8CQBb1qI4Eb9EgBQVek5gC5sXyM=",
"dev": true,
"requires": {
"esprima": "~1.1.1",
"estraverse": "~1.5.0",
"esutils": "~1.0.0",
"source-map": "~0.1.33"
}
},
"esprima": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz",
"integrity": "sha1-W28VR/TRAuZw4UDFCb5ncdautUk=",
"dev": true
},
"estraverse": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz",
"integrity": "sha1-hno+jlip+EYYr7bC3bzZFrfLr3E=",
"dev": true
},
"esutils": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz",
"integrity": "sha1-gVHTWOIMisx/t0XnRywAJf5JZXA=",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"jison": {
"version": "0.4.18",
"resolved": "https://registry.npmjs.org/jison/-/jison-0.4.18.tgz",
"integrity": "sha512-FKkCiJvozgC7VTHhMJ00a0/IApSxhlGsFIshLW6trWJ8ONX2TQJBBz6DlcO1Gffy4w9LT+uL+PA+CVnUSJMF7w==",
"dev": true,
"requires": {
"JSONSelect": "0.4.0",
"cjson": "0.3.0",
"ebnf-parser": "0.1.10",
"escodegen": "1.3.x",
"esprima": "1.1.x",
"jison-lex": "0.3.x",
"lex-parser": "~0.1.3",
"nomnom": "1.5.2"
}
},
"jison-lex": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/jison-lex/-/jison-lex-0.3.4.tgz",
"integrity": "sha1-gcoo2E+ESZ36jFlNzePYo/Jux6U=",
"dev": true,
"requires": {
"lex-parser": "0.1.x",
"nomnom": "1.5.2"
}
},
"jsonlint": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.0.tgz",
"integrity": "sha1-iKpGvCiaesk7tGyuLVihh6m7SUo=",
"dev": true,
"requires": {
"JSV": ">= 4.0.x",
"nomnom": ">= 1.5.x"
}
},
"lex-parser": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/lex-parser/-/lex-parser-0.1.4.tgz",
"integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=",
"dev": true
},
"nomnom": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.5.2.tgz",
"integrity": "sha1-9DRUSKhTz71cDSYyDyR3qwUm/i8=",
"dev": true,
"requires": {
"colors": "0.5.x",
"underscore": "1.1.x"
},
"dependencies": {
"underscore": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz",
"integrity": "sha1-QLq4S60Z0jAJbo1u9ii/8FXYPbA=",
"dev": true
}
}
},
"source-map": {
"version": "0.1.43",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
"integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
"dev": true,
"optional": true,
"requires": {
"amdefine": ">=0.0.4"
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"requires": {
"has-flag": "^3.0.0"
}
},
"test": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/test/-/test-0.6.0.tgz",
"integrity": "sha1-WYasRF7Bd1QyJRLRBLoyyKY+k44=",
"dev": true,
"requires": {
"ansi-font": "0.0.2"
}
},
"uglify-js": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz",
"integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==",
"dev": true,
"requires": {
"commander": "~2.20.0",
"source-map": "~0.6.1"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"underscore": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz",
"integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg=="
}
}
}

View File

@@ -1,6 +1,6 @@
{
"author": "Zach Carter <zach@carter.name> (http://zaa.ch)",
"name": "jsonlint",
"name": "jsonlint-mod",
"description": "Validate JSON",
"keywords": [
"json",
@@ -8,16 +8,16 @@
"lint",
"jsonlint"
],
"version": "1.6.0",
"version": "1.7.6",
"preferGlobal": true,
"repository": {
"type": "git",
"url": "git://github.com/zaach/jsonlint.git"
"url": "git://github.com/circlecell/jsonlint.git"
},
"bugs": {
"url": "http://github.com/zaach/jsonlint/issues"
"url": "http://github.com/circlecell/jsonlint/issues"
},
"main": "lib/jsonlint.js",
"main": "web/jsonlint.js",
"bin": {
"jsonlint": "lib/cli.js"
},
@@ -25,8 +25,9 @@
"node": ">= 0.6"
},
"dependencies": {
"nomnom": ">= 1.5.x",
"JSV": ">= 4.0.x"
"JSV": "^4.0.2",
"chalk": "^2.4.2",
"underscore": "^1.9.1"
},
"devDependencies": {
"test": "*",
@@ -34,8 +35,10 @@
"uglify-js": "*"
},
"scripts": {
"test": "node test/all-tests.js"
"test": "node test/all-tests.js",
"bundle": "node scripts/bundle > web/jsonlint.js",
"prepublish": "npm run bundle"
},
"homepage": "http://zaach.github.com/jsonlint/",
"homepage": "http://jsonlint.com",
"optionalDependencies": {}
}

View File

@@ -1,8 +1,8 @@
var fs = require('fs');
var source = "var jsonlint = (function(){var require=true,module=false;var exports={};" +
fs.readFileSync(__dirname+'/../lib/doug-json-parse.js', 'utf8') +
fs.readFileSync(__dirname+'/../lib/jsonlint.js', 'utf8') +
"return exports;})()";
"return exports;})();if(typeof module === 'object' && module.exports) module.exports = jsonlint;";
console.log(source);

View File

@@ -213,6 +213,16 @@ exports["test extra brace"] = function () {
assert["throws"](function () {parser.parse(json)}, "should throw error");
}
exports["test hidden characters"] = function () {
var json = fs.readFileSync(__dirname + "/fails/35.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
}
exports["test key duplicates"] = function () {
var json = fs.readFileSync(__dirname + "/fails/36.json").toString();
assert["throws"](function () {parser.parse(json)}, "should throw error");
}
exports["test pass-1"] = function () {
var json = fs.readFileSync(__dirname + "/passes/1.json").toString();
assert.doesNotThrow(function () {parser.parse(json)}, "should pass");

72
test/fails/35.json Normal file
View File

@@ -0,0 +1,72 @@
{
   "chapters":[
        {
            "title":"Unit Opener",
            "file":"index1.html"
        },
        {
            "title":"Reading",
            "file":"index2.html"
        },
        {
            "title":"Reading Comprehension",
            "file":"index3.html"
        },
        {
            "title":"Vocabulary List",
            "file":"index4.html"
        },
        {
            "title":"Mind Map",
            "file":"index5.html"
        },
        {
            "title":"Vocabulary and Spelling",
            "file":"index6.html"
        },
        {
            "title":"Language Arts",
            "file":"index7.html"
        },
        {
            "title":"Grammar",
            "file":"index8.html"
        },
{
            "title":"Reading 2",
            "file":"index9.html"
        },
        {
            "title":"Reading Comprehension and Vocabulary",
            "file":"index10.html"
        },
        {
            "title":"Let's Play",
            "file":"index11.html"
        },
        {
            "title":"Investigate and Report",
            "file":"index12.html"
        },
        {
            "title":"Grammar Review",
            "file":"index13.html"
        },
        {
            "title":"Everyday English",
            "file":"index14.html"
        },
        {
            "title":"Test Yourself: Reading",
            "file":"index15.html"
        },
        {
            "title":"Test Yourself: Writing",
            "file":"index16.html"
        },
{
            "title":"Logic: Allegory",
            "file":"index16.html"
        }
    ]
}

2
test/fails/36.json Normal file
View File

@@ -0,0 +1,2 @@
{"x":1,
"x":2}

File diff suppressed because one or more lines are too long