pax_global_header00006660000000000000000000000064124021512770014513gustar00rootroot0000000000000052 comment=f4b604072457982d73e886e78a423e2336db6fc8 jju-1.1.0/000077500000000000000000000000001240215127700123025ustar00rootroot00000000000000jju-1.1.0/.eslint.yaml000066400000000000000000000141071240215127700145450ustar00rootroot00000000000000env: node: true # # 0 - disable # Rules that more harmful than useful, or just buggy. # # 1 - warning # Rules that we didn't encounter yet. You can safely ignore them, # but I'd like to know any interesting use-cases they forbid. # # 2 - error # Rules that have proven to be useful, please follow them. # rules: # didn't understand what it does, but it fails a good code block-scoped-var: 0 # fails where newlines are used to format pretty big "if": # if ( # name.charAt(0) === "." || # name.match(/[\/@\s\+%:]/) || # name !== encodeURIComponent(name) || # name.toLowerCase() === "node_modules" # ) { brace-style: 1 # snake_case is more readable, what's up with you guys? camelcase: 0 # if some functions are complex, they are for a good reason, # ain't worth it complexity: [0, 10] # never saw it, but self is preferred consistent-this: [1, self] # fails good code curly: [0, multi] # fails good code, where this notation is used for consistency: # something['foo-bar'] = 123 # something['blahblah'] = 234 dot-notation: 0 # pointless in many cases (like indexOf() == -1), harmful in a few # cases (when you do want to ignore types), fails good code eqeqeq: 0 # if someone is changing prototype and makes properties enumerable, # it's their own fault guard-for-in: 0 # if some functions are complex, they are for a good reason, # ain't worth it max-depth: [0, 4] max-nested-callbacks: [0, 2] # should it really throw for every long URL? max-len: [0, 80, 4] # that's obvious by just looking at the code, you don't need lint for that max-params: [0, 3] # if some functions are complex, they are for a good reason, # ain't worth it max-statements: [0, 10] # that one makes sense new-cap: 2 # I'm writing javascript, not some weird reduced version of it no-bitwise: 0 # not working around IE bugs, sorry no-catch-shadow: 0 # see above, IE is useful for downloading other browsers only no-comma-dangle: 0 # good for removing debugging code no-console: 2 # good for removing debugging code no-debugger: 2 # why would anyone need to check against that? no-else-return: 0 # sometimes empty statement contains useful comment no-empty: 0 # stupid rule # "x == null" is "x === null || x === undefined" no-eq-null: 0 # fails good code, when parens are used for grouping: # (req && req.headers['via']) ? req.headers['via'] + ', ' : '' # not everyone remembers priority tables, you know no-extra-parens: 0 # fails defensive semicolons: # ;['foo', 'bar'].forEach(function(x) {}) no-extra-semi: 0 # fails good code: # var fs = require('fs'), # , open = fs.open no-mixed-requires: [0, false] # new Array(12) is used to pre-allocate arrays no-new-array: 0 # fails good code: # fs.open('/file', 0666, function(){}) no-octal: 0 # fails good code: # console.log('\033[31m' + str + '\033[39m') # also fails \0 which is not octal escape no-octal-escape: 0 # I'm writing javascript, not some weird reduced version of it no-plusplus: 0 # fails good code: # if (a) { # var x = 'foo' # } else { # var x = bar # } no-redeclare: 0 # sometimes useful, often isn't # probably worth enforcing no-shadow: 2 no-sync: 2 # I'm writing javascript, not some weird reduced version of it no-ternary: 0 # the single most important rule in the entire ruleset no-undef: 2 # it is failing our own underscores no-underscore-dangle: 0 # fails function hoisting no-unreachable: 0 # fails npm-style code, it's good once you get used to it: # if (typeof(options) === 'function') callback = options, options = {} no-unused-expressions: 0 # fails (function(_err) {}) where named argument is used to show what # nth function argument means no-unused-vars: [0, local] # fails function hoisting no-use-before-define: 0 # fails foobar( (function(){}).bind(this) ) # parens are added for readability no-wrap-func: 0 # fails good code: # var x # if (something) { # var y one-var: 0 quote-props: 0 # fails situation when different quotes are used to avoid escaping quotes: [2, single, avoid-escape] # http:#blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding semi: [2, never] # fails good code where spaces are used for grouping: # (x+y * y+z) space-infix-ops: 0 # typeof(something) should have braces to look like a function # a matter of taste I suppose space-unary-word-ops: 0 # strict mode is just harmful, # can I have a check to enforce not using it? strict: 0 sort-vars: 0 no-path-concat: 0 func-names: 0 # how can you set a return code without process.exit? no-process-exit: 0 # both styles are useful func-style: [0, declaration] # fails while(1) {...} no-constant-condition: 0 # fails good code: # https://github.com/rlidwka/jju/blob/eb52ee72e5f21d48963798f9bda8ac8d68082148/lib/parse.js#L732 no-ex-assign: 0 wrap-iife: [2, inside] consistent-return: 1 new-parens: 1 no-alert: 1 no-array-constructor: 1 no-caller: 1 no-cond-assign: 1 no-control-regex: 1 no-delete-var: 1 no-div-regex: 1 no-dupe-keys: 1 no-empty-class: 1 no-empty-label: 1 no-eval: 1 no-extend-native: 1 no-extra-boolean-cast: 1 no-extra-strict: 1 no-fallthrough: 1 no-floating-decimal: 1 no-func-assign: 1 no-global-strict: 1 no-implied-eval: 1 no-invalid-regexp: 1 no-iterator: 1 no-labels: 1 no-label-var: 1 no-lone-blocks: 1 no-loop-func: 1 no-multi-str: 1 no-native-reassign: 1 no-negated-in-lhs: 1 no-nested-ternary: 1 no-new: 1 no-new-func: 1 no-new-object: 1 no-new-wrappers: 1 no-obj-calls: 1 no-octal: 1 no-proto: 1 no-regex-spaces: 1 no-return-assign: 1 no-script-url: 1 no-self-compare: 1 no-shadow: 1 no-shadow-restricted-names: 1 no-spaced-func: 1 no-sparse-arrays: 1 no-sync: 1 no-undef: 1 no-undef-init: 1 no-unreachable: 1 no-with: 1 no-yoda: 1 radix: 1 space-return-throw-case: 1 use-isnan: 1 valid-jsdoc: 1 wrap-regex: 1 jju-1.1.0/.gitignore000066400000000000000000000001101240215127700142620ustar00rootroot00000000000000package.json node_modules test/external/parse-cases test/external/v8.js jju-1.1.0/.npmignore000066400000000000000000000001101240215127700142710ustar00rootroot00000000000000package.json node_modules test/external examples /.eslint* /.travis.yml jju-1.1.0/.travis.yml000066400000000000000000000002261240215127700144130ustar00rootroot00000000000000language: node_js node_js: - '0.10' - '0.11' matrix: allow_failures: - node_js: "0.11" fast_finish: true script: npm install . && npm test jju-1.1.0/README.md000066400000000000000000000205361240215127700135670ustar00rootroot00000000000000`jju` - a set of utilities to work with JSON / JSON5 documents [![npm version badge](https://img.shields.io/npm/v/jju.svg)](https://www.npmjs.org/package/jju) [![travis badge](http://img.shields.io/travis/rlidwka/jju.svg)](https://travis-ci.org/rlidwka/jju) [![downloads badge](http://img.shields.io/npm/dm/jju.svg)](https://www.npmjs.org/package/jju) ## Installation ``` npm install jju ``` ## Usage This module provides following functions: 1. [jju.parse()](#jjuparse-function) parses json/json5 text and returns a javascript value it corresponds to 2. [jju.stringify()](#jjustringify-function) converts javascript value to an appropriate json/json5 text 3. [jju.tokenize()](#jjutokenize-function) parses json/json5 text and returns an array of tokens it consists of ([see demo](http://rlidwka.github.io/jju/tokenizer.html)) 4. [jju.analyze()](#jjuanalyze-function) parses json/json5 text and tries to guess indentation, quoting style, etc. 5. [jju.update()](#jjuupdate-function) changes json/json5 text, preserving original formatting as much as possible ([see demo](http://rlidwka.github.io/jju/editor.html)) All functions are able to work with a standard JSON documents. `jju.parse()` and `jju.stringify()` are better in some cases, but slower than native `JSON.parse()` and `JSON.stringify()` versions. Detailed description see below. ### jju.parse() function ```javascript /* * Main syntax: * * `text` - text to parse, type: String * `options` - parser options, type: Object */ jju.parse(text[, options]) // compatibility syntax jju.parse(text[, reviver]) ``` Options: - reserved\_keys - what to do with reserved keys (String, default="ignore") - "ignore" - ignore reserved keys - "throw" - throw SyntaxError in case of reserved keys - "replace" - replace reserved keys, this is the default JSON.parse behaviour, unsafe Reserved keys are keys that exist in an empty object (`hasOwnProperty`, `__proto__`, etc.). ```javascript // 'ignore' will cause reserved keys to be ignored: parse('{hasOwnProperty: 1}', {reserved_keys: 'ignore'}) == {} parse('{hasOwnProperty: 1, x: 2}', {reserved_keys: 'ignore'}).hasOwnProperty('x') == true // 'throw' will cause SyntaxError in these cases: parse('{hasOwnProperty: 1}', {reserved_keys: 'throw'}) == SyntaxError // 'replace' will replace reserved keys with new ones: parse('{hasOwnProperty: 1}', {reserved_keys: 'throw'}) == {hasOwnProperty: 1} parse('{hasOwnProperty: 1, x: 2}', {reserved_keys: 'ignore'}).hasOwnProperty('x') == TypeError ``` - null\_prototype - create object as Object.create(null) instead of '{}' (Boolean) if `reserved_keys != 'replace'`, default is **false** if `reserved_keys == 'replace'`, default is **true** It is usually unsafe and not recommended to change this option to false in the last case. - reviver - reviver function - Function This function should follow JSON specification - mode - operation mode, set it to 'json' if you want to throw on non-strict json files (String) ### jju.stringify() function ```javascript /* * Main syntax: * * `value` - value to serialize, type: * * `options` - serializer options, type: Object */ jju.stringify(value[, options]) // compatibility syntax jju.stringify(value[, replacer [, indent]) ``` Options: - ascii - output ascii only (Boolean, default=false) If this option is enabled, output will not have any characters except of 0x20-0x7f. - indent - indentation (String, Number or Boolean, default='\t') This option follows JSON specification. - quote - enquoting char (String, "'" or '"', default="'") - quote\_keys - whether keys quoting in objects is required or not (String, default=false) If you want `{"q": 1}` instead of `{q: 1}`, set it to true. - sort\_keys - sort all keys while stringifying (Boolean or Function, default=false) By default sort order will depend on implementation, with v8 it's insertion order. If set to `true`, all keys (but not arrays) will be sorted alphabetically. You can provide your own sorting function as well. - replacer - replacer function or array (Function or Array) This option follows JSON specification. - no\_trailing\_comma = don't output trailing comma (Boolean, default=false) If this option is set, arrays like this `[1,2,3,]` will never be generated. Otherwise they may be generated for pretty printing. - mode - operation mode, set it to 'json' if you want correct json in the output (String) Currently it's either 'json' or something else. If it is 'json', following options are implied: - options.quote = '"' - options.no\_trailing\_comma = true - options.quote\_keys = true - '\x' literals are not used ### jju.tokenize() function ```javascript /* * Main syntax: * * `text` - text to tokenize, type: String * `options` - parser options, type: Object */ jju.tokenize(text[, options]) ``` Options are the same as for the `jju.parse` function. Return value is an array of tokens, where each token is an object: - raw (String) - raw text of this token, if you join all raw's, you will get the original document - type (String) - type of the token, can be `whitespace`, `comment`, `key`, `literal`, `separator` or `newline` - stack (Array) - path to the current token in the syntax tree - value - value of the token if token is a `key` or `literal` You can check tokenizer for yourself using [this demo](http://rlidwka.github.io/jju/tokenizer.html). ### jju.analyze() function ```javascript /* * Main syntax: * * `text` - text to analyze, type: String * `options` - parser options, type: Object */ jju.analyze(text[, options]) ``` Options are the same as for the `jju.parse` function. Return value is an object defining a programming style in which the document was written. - indent (String) - preferred indentation - newline (String) - preferred newline - quote (String) - `"` or `'` depending on which quote is preferred - quote\_keys (Boolean) - `true` if unquoted keys were used at least once - has\_whitespace (Boolean) - `true` if input has a whitespace token - has\_comments (Boolean) - `true` if input has a comment token - has\_newlines (Boolean) - `true` if input has a newline token - has\_trailing\_comma (Boolean) - `true` if input has at least one trailing comma ### jju.update() function ```javascript /* * Main syntax: * * `text` - original text, type: String * `new_value` - new value you want to set * `options` - parser or stringifier options, type: Object */ jju.update(text, new_value[, options]) ``` If you want to update a JSON document, here is the general approach: ```javascript // here is your original JSON document: var input = '{"foo": "bar", "baz": 123}' // you need to parse it first: var json = jju.parse(input, {mode: 'json'}) // json is { foo: 'bar', baz: 123 } // then you can change it as you like: json.foo = 'quux' json.hello = 'world' // then you run an update function to change the original json: var output = jju.update(input, json, {mode: 'json'}) // output is '{"foo": "quux", "baz": 123, "hello": "world"}' ``` Look at [this demo](http://rlidwka.github.io/jju/editor.html) to test various types of json. ## Advantages over existing JSON libraries In a few cases it makes sense to use this module instead of built-in JSON methods. Parser: - better error reporting with source code and line numbers In case of syntax error, JSON.parse does not return any good information to the user. This module does: ``` $ node -e 'require("jju").parse("[1,1,1,1,invalid]")' SyntaxError: Unexpected token 'i' at 0:9 [1,1,1,1,invalid] ^ ``` This module is about 5 times slower, so if user experience matters to you more than performance, use this module. If you're working with a lot of machine-generated data, use JSON.parse instead. Stringifier: - util.inspect-like pretty printing This module behaves more smart when dealing with object and arrays, and does not always print newlines in them: ``` $ node -e 'console.log(require("./").stringify([[,,,],,,[,,,,]], {mode:"json"}))' [ [null, null, null], null, null, [null, null, null, null] ] ``` JSON.stringify will split this into 15 lines, and it's hard to read. Yet again, this feature comes with a performance hit, so if user experience matters to you more than performance, use this module. If your JSON will be consumed by machines, use JSON.stringify instead. As a rule of thumb, if you use "space" argument to indent your JSON, you'd better use this module instead. jju-1.1.0/docs/000077500000000000000000000000001240215127700132325ustar00rootroot00000000000000jju-1.1.0/docs/Grammar.md000066400000000000000000000161301240215127700151430ustar00rootroot00000000000000 JSON5 grammar expressed in EBNF form. PS: I don't know what is appropriate syntax highlighter for this, so I'm using "modula2" because why not. I also inserted after backslash to preserve syntax highlighting, this character has nothing to do with actual JSON5 syntax and should be ignored. ```modula2 json5_text = expression_with_whitespace expression_with_whitespace = [white_space] , expression , [white_space] expression = literal | array_literal | object_literal literal = null_literal | boolean_literal | signed_numeric_literal | string_literal null_literal = 'null' boolean_literal = 'true' | 'false' (* Source Characters *) source_character = . (* any Unicode code unit *) line_terminator = | | | line_terminator_sequence = | | | | , white_space = white_space_element | white_space , white_space_element white_space_element = white_space_character | comment white_space_character = | | | | | | comment = multi_line_comment | single_line_comment multi_line_comment = '/*' , [multi_line_comment_chars] , '*/' multi_line_comment_chars = (source_character - '*') , [multi_line_comment_chars] | '*' , [post_asterisk_comment_chars] post_asterisk_comment_chars = (source_character - ('*' | '/')) , [multi_line_comment_chars] | '*' , [post_asterisk_comment_chars] single_line_comment = '//' , [single_line_comment_chars] single_line_comment_chars = single_line_comment_char , single_line_comment_chars single_line_comment_char = source_character - line_terminator (* Character classes *) decimal_digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' non_zero_digit = decimal_digit - '0' hex_digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' ascii_letter = ascii_letter_lowercase | ascii_letter_uppercase ascii_letter_lowercase = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' ascii_letter_uppercase = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' (* Numeric Literals *) signed_numeric_literal = '-' , numeric_literal | '+' , numeric_literal | numeric_literal numeric_literal = decimal_literal | hex_integer_literal | non_finite_literal non_finite_literal = 'Infinity' | 'NaN' decimal_literal = decimal_integer_literal , '.' , [decimal_digits] , [exponent_part] | '.' , decimal_digits , [exponent_part] | decimal_integer_literal , [exponent_part] decimal_integer_literal = '0' | non_zero_digit , [decimal_digits] decimal_digits = decimal_digit | decimal_digits , decimal_digit exponent_part = exponent_indicator , signed_integer exponent_indicator = 'e' | 'E' signed_integer = decimal_digits | '+' , decimal_digits | '-' , decimal_digits hex_integer_literal = '0x' , hex_digit | '0X' , hex_digit | hex_integer_literal , hex_digit (* String Literals *) string_literal = '"' , [double_string_characters] , '"' | "'" , [single_string_characters] , "'" double_string_characters = double_string_character , [double_string_characters] single_string_characters = single_string_character , [single_string_characters] double_string_character = source_character - ('"' | '\​' | line_terminator) | '\​' , escape_sequence | line_continuation single_string_character = source_character - ("'" | '\​' | line_terminator) | '\​' , escape_sequence | line_continuation line_continuation = '\​' , line_terminator_sequence escape_sequence = character_escape_sequence | '0' | hex_escape_sequence | unicode_escape_sequence character_escape_sequence = single_escape_character | non_escape_character single_escape_character = '"' | "'" | '\​' | 'b' | 'f' | 'n' | 'r' | 't' | 'v' non_escape_character = source_character - (escape_character | line_terminator) escape_character = single_escape_character | decimal_digit | 'x' | 'u' hex_escape_sequence = 'x' , hex_digit , hex_digit unicode_escape_sequence = 'u' , hex_digit , hex_digit , hex_digit , hex_digit (* Array Literals *) array_literal = '[' , [white_space] , ']' | '[' , [white_space] , element_list , ']' | '[' , [white_space] , element_list , ',' , [white_space] , ']' element_list = expression , [white_space] | element_list , ',' , [white_space] , expression , [white_space] (* Object Literals *) object_literal = '{' , [white_space] , '}' | '{' , [white_space] , property_name_and_value_list , '}' | '{' , [white_space] , property_name_and_value_list , ',' , '}' property_name_and_value_list = property_assignment , [white_space] | property_name_and_value_list , [white_space] , ',' , [white_space] , property_assignment , [white_space] property_assignment = property_name , [white_space] , ':' , [white_space] , expression property_name = identifier_name | string_literal | numeric_literal identifier_name = identifier_start | identifier_name , identifier_part identifier_start = unicode_letter | '$' | '_' | '\​' , unicode_escape_sequence identifier_part = identifier_start | unicode_combining_mark | unicode_digit | unicode_connector_punctuation | | unicode_letter = ascii_letter (* + any character in the Unicode categories "Uppercase letter (Lu)", "Lowercase letter (Ll)", "Titlecase letter (Lt)", "Modifier letter (Lm)", "Other letter (Lo)", or "Letter number (Nl)" *) unicode_combining_mark = (* + any character in the Unicode categories "Non-spacing mark (Mn)" or "Combining spacing mark (Mc)" *) unicode_digit = decimal_digit (* + any character in the Unicode category "Decimal number (Nd)" *) unicode_connector_punctuation = (* + any character in the Unicode category "Connector punctuation (Pc)" *) ``` jju-1.1.0/docs/JSON5.md000066400000000000000000000046031240215127700144150ustar00rootroot00000000000000## JSON5 syntax We support slighly modified version of JSON5, see https://groups.google.com/forum/#!topic/json5/3DjClVYI6Wg I started from ES5 specification and added a set of additional restrictions on top of ES5 spec. So I'd expect my implementation to be much closer to javascript. It's no longer an extension of json, but a reduction of ecmascript, which was my original intent. This JSON5 version is a subset of ES5 language, specification is here: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf This is a language that defines data structures only, so following notes/restrictions are applied: - Literals (NullLiteral, BooleanLiteral, NumericLiteral, StringLiteral) are allowed. - Compatibility syntax is not supported, which means octal literals are forbidden. - ArrayLiterals and allowed, but instead of AssignmentExpressions you can only use other allowed Literals, ArrayLiterals and ObjectLiterals. Elisions are currently not supported. - ObjectLiterals and allowed, but instead of AssignmentExpressions you can only use other allowed Literals, ArrayLiterals and ObjectLiterals. Setters and getters are forbidden. - All other primary expressions ("this", Identifier, Expression) are forbidden. - Two unary expressions ('-' and '+') allowed before NumericLiterals. - Any data that has a number type can be represented, including +0, -0, +Infinity, -Infinity and NaN. - "undefined" is forbidden, use null instead if applicable. - Comments and whitespace are defined according to spec. Main authority here is ES5 spec, so strict backward JSON compatibility is not guaranteed. If you're unsure whether a behaviour of this library is a bug or not, you can run this test: ```javascript JSON5.parse(String(something)) ``` Should always be equal to: ```javascript eval('(function(){return ('+String(something)+'\n)\n})()') ``` If `something` meets all rules above. Parens and newlines in the example above are carefully placed so comments and another newlines will work properly, so don't look so impressed about that. ## Weirdness of JSON5 These are the parts that I don't particulary like, but see no good way to fix: - no elisions, `[,,,] -> [null,null,null]` - `[Object], [Circular]` aren't parsed - no way of nicely representing multiline strings - unicode property names are way to hard to implement - Date and other custom objects - incompatible with YAML (at least comments) jju-1.1.0/examples/000077500000000000000000000000001240215127700141205ustar00rootroot00000000000000jju-1.1.0/examples/README.md000066400000000000000000000013561240215127700154040ustar00rootroot00000000000000 These are the examples for some common use-cases. ## convert This simple example converts JSON5 into JSON. Nothing too fancy. ## jsonlint This example shows how to ensure that your JSON is a valid, and display an appropriate error message if it's not. It also shows how to get line numbers for a possible error, since JSON.parse doesn't provide this information. ## strip-comments This example removes comments from a JSON file to process it with JSON.parse later. It is pretty much useless as is, but it shows what tokenize function can do. ## update-json This example shows how to update your json gracefully. You can format your JSON file as you like, and we can update it preserving it's initial formatting (newlines, spaces, etc.). jju-1.1.0/examples/convert/000077500000000000000000000000001240215127700156005ustar00rootroot00000000000000jju-1.1.0/examples/convert/convert.js000077500000000000000000000005161240215127700176230ustar00rootroot00000000000000#!/usr/bin/env node var fs = require('fs') , jju = require('../../') if (process.argv.length != 3) { console.log([ 'Usage: convert.js ' ].join('\n')) process.exit(1) } var contents = fs.readFileSync(process.argv[2], 'utf8') var parsed = jju.parse(contents) console.log(jju.stringify(parsed, { mode: 'json' })) jju-1.1.0/examples/convert/package.json5000066400000000000000000000100001240215127700201420ustar00rootroot00000000000000// vim:syntax=javascript { /* * Main info */ name: 'yapm', version: '1.1.0-1325', // upstream npm@1.3.25 description: 'A package manager for node (npm fork)', author: { // forked from npm, author: Isaac Z. Schlueter name: 'Alex Kocharin', email: 'alex@kocharin.ru', }, /* * WWW Links */ homepage: 'https://npmjs.org/doc/', repository: { type: 'git', url: 'https://github.com/rlidwka/yapm', }, bugs: { url: 'http://github.com/rlidwka/yapm/issues', }, /* * File structure */ directories: { doc: './doc', lib: './lib', bin: './bin', // not providing any man pages since they // seem to conflict with npm installation //man: './man', }, main: './lib/npm.js', bin: './bin/npm-cli.js', scripts: { test: 'node ./test/run.js && tap test/tap/*.js', tap: 'tap test/tap/*.js', prepublish: 'node bin/npm-cli.js prune --prefix=. --no-global && rm -rf test/*/*/node_modules && make -j32 doc', dumpconf: 'env | grep npm | sort | uniq', echo: 'node bin/npm-cli.js', }, /* * Dependencies list */ dependencies: { 'semver': '~2.2.1', 'ini': '~1.1.0', 'slide': '~1.1.5', 'abbrev': '~1.0.4', 'graceful-fs': '~2.0.0', 'minimatch': '~0.2.14', 'nopt': '~2.1.2', 'rimraf': '~2.2.5', 'request': '~2.30.0', 'which': '1', 'tar': '~0.1.19', 'fstream': '~0.1.25', 'block-stream': '0.0.7', 'mkdirp': '~0.3.5', 'read': '~1.0.4', 'lru-cache': '~2.5.0', 'node-gyp': '~0.12.2', 'fstream-npm': '~0.1.6', 'uid-number': '0', 'archy': '0', 'chownr': '0', 'npmlog': '0.0.6', 'ansi': '~0.2.1', 'npm-registry-client': '~0.3.5', 'read-package-json': '~1.1.6', 'read-installed': '~0.2.2', 'glob': '~3.2.6', 'init-package-json': '0.0.14', 'osenv': '0', 'lockfile': '~0.4.0', 'retry': '~0.6.0', 'once': '~1.3.0', 'npmconf': '~0.1.12', 'opener': '~1.3.0', 'chmodr': '~0.1.0', 'cmd-shim': '~1.1.1', 'sha': '~1.2.1', 'editor': '0.0.5', 'child-process-close': '~0.1.1', 'npm-user-validate': '0.0.3', 'github-url-from-git': '1.1.1', 'github-url-from-username-repo': '0.0.2', 'text-table': '~0.2.0', 'ansicolors': '~0.3.2', 'ansistyles': '~0.1.3', 'path-is-inside': '~1.0.0', 'columnify': '0.1.2', // visionmedia/npm deps 'chalk': '~0.4.0', 'gh-lookup': '~0.4.0', 'printf': '~0.1.2', // our own deps 'js-yaml': '*', // optional? 'through': '*', // can be dropped in node 0.10+ 'jju': '*', 'yaml-update': '*', }, bundleDependencies: [ 'semver', 'ini', 'slide', 'abbrev', 'graceful-fs', 'minimatch', 'nopt', 'rimraf', 'request', 'which', 'tar', 'fstream', 'block-stream', 'inherits', 'mkdirp', 'read', 'lru-cache', 'node-gyp', 'fstream-npm', 'uid-number', 'archy', 'chownr', 'npmlog', 'ansi', 'npm-registry-client', 'read-package-json', 'read-installed', 'glob', 'init-package-json', 'osenv', 'lockfile', 'retry', 'once', 'npmconf', 'opener', 'chmodr', 'cmd-shim', 'sha', 'child-process-close', 'editor', 'npm-user-validate', 'github-url-from-git', 'github-url-from-username-repo', 'normalize-package-data', 'text-table', 'ansicolors', 'ansistyles', 'path-is-inside', 'columnify', 'js-yaml', 'through', 'jju', 'yaml-update', 'chalk', 'gh-lookup', 'printf', ], devDependencies: { ronn: '~0.3.6', tap: '~0.4.0', 'npm-registry-mock': '~0.5.5', }, /* * Other stuff */ publishConfig: { 'proprietary-attribs': false, }, keywords: [ 'package manager', 'modules', 'install', 'package.yaml', 'package.json5', 'yaml', 'json5', 'npm', ], preferGlobal: true, config: { publishtest: false, }, engines: { node: '>=0.6', npm: '1', }, license: 'Artistic-2.0', } jju-1.1.0/examples/jsonlint/000077500000000000000000000000001240215127700157605ustar00rootroot00000000000000jju-1.1.0/examples/jsonlint/jsonlint.js000077500000000000000000000005571240215127700201700ustar00rootroot00000000000000#!/usr/bin/env node var fs = require('fs') , jju = require('../../') if (process.argv.length != 3) { console.log([ 'Usage: jsonlint.js ' ].join('\n')) process.exit(1) } var contents = fs.readFileSync(process.argv[2], 'utf8') try { jju.parse(contents, {mode: 'json'}) console.log('Your JSON looks good!') } catch(e) { console.log(e.message) } jju-1.1.0/examples/jsonlint/package.json000066400000000000000000000004011240215127700202410ustar00rootroot00000000000000{ "name": "jju", "description": "a set of utilities to operate on JSON / JSON5 documents", "repository": { "type": "git", "url" "git://github.com/rlidwka/jju" } "devDependencies": { "mocha": "*" }, "scripts": { "test": "mocha test/*.js" } } jju-1.1.0/examples/strip-comments/000077500000000000000000000000001240215127700171045ustar00rootroot00000000000000jju-1.1.0/examples/strip-comments/example.json000066400000000000000000000007361240215127700214400ustar00rootroot00000000000000/* * This is an example of commented JSON (CJSON) file * * Source: https://en.wikipedia.org/wiki/JSON */ { "firstName": "John", "lastName": "Smith", "age": 25, // this is an address "address": { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": 10021 }, // these are phone numbers (duh) "phoneNumbers": [ { "type": "home", "number": "212 555-1234" }, { "type": "fax", "number": "646 555-4567" } ] } jju-1.1.0/examples/strip-comments/strip-comments.js000077500000000000000000000011401240215127700224250ustar00rootroot00000000000000#!/usr/bin/env node var fs = require('fs') , jju = require('../../') if (process.argv.length != 3) { console.log([ 'Usage: strip-comments.js ' ].join('\n')) process.exit(1) } var contents = fs.readFileSync(process.argv[2], 'utf8') var tokens = jju.tokenize(contents) var stripped = tokens.filter(function(t) { // filter out "comments" type of tokens // you can also strip newlines and separators the same way if you want return t.type !== 'comment' }).map(function(t) { // extract contents from the token to join them together later return t.raw }).join('') console.log(stripped) jju-1.1.0/examples/update-json/000077500000000000000000000000001240215127700163515ustar00rootroot00000000000000jju-1.1.0/examples/update-json/conf.json000066400000000000000000000003651240215127700201750ustar00rootroot00000000000000{ "dependsOn": "0.10.0", "commands": { "www": "./commands/www", "console": "./commands/repl" }, "generators": { "options": { "engine": "ejs" }, "modules": { "new": "generate-new", "backend": "generate-backend" } } } jju-1.1.0/examples/update-json/config.js000077500000000000000000000016421240215127700201620ustar00rootroot00000000000000#!/usr/bin/env node var fs = require('fs') , jju = require('../../') , dottie = require('dottie') function showhelp() { console.log([ 'Usage:', ' config.js - set config key to value', ' config.js - retrieve config key', '', 'Example:', ' config.js set commands.www 12345', ' config.js get commands.www', ' config.js del commands.www', ].join('\n')) process.exit(1) } if (process.argv.length <= 2) { showhelp() } var contents = fs.readFileSync('conf.json', 'utf8') var json = jju.parse(contents, {mode: 'json'}) if (process.argv[2] === 'get') { console.log(dottie.get(json, process.argv[3])) } else if (process.argv[2] === 'set' || process.argv[2] === 'del') { dottie.set(json, process.argv[3], process.argv[4]) var newcontents = jju.update(contents, json, {mode: 'json'}) console.log(newcontents) //fs.writeFileSync(newcontents, 'conf.json') } else { showhelp() } jju-1.1.0/examples/update-json/node_modules/000077500000000000000000000000001240215127700210265ustar00rootroot00000000000000jju-1.1.0/examples/update-json/node_modules/dottie/000077500000000000000000000000001240215127700223165ustar00rootroot00000000000000jju-1.1.0/examples/update-json/node_modules/dottie/README.md000066400000000000000000000023431240215127700235770ustar00rootroot00000000000000[![Build Status](https://secure.travis-ci.org/innofluence/dottie.js.png)](http://travis-ci.org/innofluence/dottie.js) ## Install npm install dottie ## Usage For detailed usage, check source or tests. ### Get value Gets nested value, or undefined if unreachable. var values = { some: { nested: { key: 'foobar'; } } } dottie.get(values, 'some.nested.key'); // returns 'foobar' dottie.get(values, 'some.undefined.key'); // returns undefined ### Set value Sets nested value, creates nested structure if needed dottie.set(values, 'some.nested.value', someValue); ### Transform object Transform object from keys with dottie notation to nested objects var values = { 'user.name': 'Mick Hansen', 'user.email': 'mh@innofluence.com', 'user.professional.title': 'Developer', 'user.professional.employer': 'Innofluence' }; var transformed = dottie.transform(values); transforms is now equal to = { user: { name: 'Mick Hansen', email: 'mh@innofluence.com', professional: { title: 'Developer', employer: 'Innofluence' } } }jju-1.1.0/examples/update-json/node_modules/dottie/dottie.js000066400000000000000000000075441240215127700241560ustar00rootroot00000000000000(function(undefined) { var root = this; // Weird IE shit, objects do not have hasOwn, but the prototype does... var hasOwnProp = Object.prototype.hasOwnProperty; // Object cloning function, uses jQuery/Underscore/Object.create depending on what's available var clone = function (object) { if (typeof Object.hasOwnProperty !== 'undefined') { var target = {}; for (var i in object) { if (hasOwnProp.call(object, i)) { target[i] = object[i]; } } return target; } if (typeof jQuery !== 'undefined') { return jQuery.extend({}, object); } if (typeof _ !== 'undefined') { return _.extend({}, object); } }; var Dottie = function() { var args = Array.prototype.slice.call(arguments); if (args.length == 2) { return Dottie.find.apply(this, args); } return Dottie.transform.apply(this, args); }; // Legacy syntax, changed syntax to have get/set be similar in arg order Dottie.find = function(path, object) { return Dottie.get(object, path); }; // Traverse object according to path, return value if found - Return undefined if destination is unreachable Dottie.get = function(object, path) { var pieces = path.split('.'), current = object, piece; if (current) { for (var index = 0, length = pieces.length; index < length; index++) { piece = pieces[index]; if (!hasOwnProp.call(current, piece)) { return undefined; } current = current[piece]; if (current === undefined) { return undefined; } } return current; } return undefined; }; // Set nested value Dottie.set = function(object, path, value) { var pieces = path.split('.'), current = object, piece, length = pieces.length; for (var index = 0; index < length; index++) { piece = pieces[index]; if (!hasOwnProp.call(current, piece) || current[piece] === undefined) { current[piece] = {}; } if (typeof current !== 'object') throw new Error('Target key is not suitable for a nested value (it is either not undefined or not an object)'); if (index == (length - 1)) { current[piece] = value; } else { current = current[piece]; } } current[piece] = value; }; // Set default nested value Dottie['default'] = function(object, path, value) { if (Dottie.get(object, path) === undefined) { Dottie.set(object, path, value); } }; // Transform unnested object with .-seperated keys into a nested object. Dottie.transform = function(object) { var pieces, piecesLength, current, transformed = clone(object), piece; for (var key in transformed) { if (key.indexOf('.') !== -1) { pieces = key.split('.'); piecesLength = pieces.length; current = transformed; for (var index in pieces) { piece = pieces[index]; if (index != (piecesLength - 1) && !current.hasOwnProperty(piece)) { current[piece] = {}; } if (index == (piecesLength - 1)) { current[piece] = object[key]; delete transformed[key]; } current = current[piece]; } } else { transformed[key] = transformed[key]; // Ensure that properties exist on the object, not just the prototype } } return transformed; }; Dottie.flatten = function(object, seperator) { if (typeof seperator === "undefined") seperator = '.'; var flattened = {}, current, nested; for (var key in object) { if (hasOwnProp.call(object, key)) { current = object[key]; if (current === Object(current)) { nested = Dottie.flatten(current, seperator); for (var _key in nested) { flattened[key+seperator+_key] = nested[_key]; } } else { flattened[key] = current; } } } return flattened; } if (typeof module !== 'undefined' && module.exports) { exports = module.exports = Dottie; } else { root['Dottie'] = Dottie; root['Dot'] = Dottie; //BC if (typeof define === "function") { define([], function () { return Dottie; }); } } })(); jju-1.1.0/examples/update-json/node_modules/dottie/package.json000066400000000000000000000040701240215127700246050ustar00rootroot00000000000000{ "name": "dottie", "version": "0.0.9-0", "devDependencies": { "expect.js": "~0.2.0", "mocha": "~1.14.0" }, "description": "Object traversing/manipulation util", "author": { "name": "Mick Hansen", "email": "maker@mhansen.io" }, "repository": { "type": "git", "url": "git://github.com/greenwoodio/dottie.js.git" }, "main": "dottie.js", "scripts": { "test": "./node_modules/mocha/bin/mocha -t 5000 -s 100 --reporter spec test/" }, "readme": "[![Build Status](https://secure.travis-ci.org/innofluence/dottie.js.png)](http://travis-ci.org/innofluence/dottie.js)\n\n## Install\n npm install dottie\n\n## Usage\nFor detailed usage, check source or tests.\n\n### Get value\nGets nested value, or undefined if unreachable.\n\n var values = {\n some: {\n nested: {\n key: 'foobar';\n }\n }\n }\n\n dottie.get(values, 'some.nested.key'); // returns 'foobar'\n dottie.get(values, 'some.undefined.key'); // returns undefined\n\n### Set value\nSets nested value, creates nested structure if needed\n\n dottie.set(values, 'some.nested.value', someValue);\n\n### Transform object\nTransform object from keys with dottie notation to nested objects\n\n var values = {\n 'user.name': 'Mick Hansen',\n 'user.email': 'mh@innofluence.com',\n 'user.professional.title': 'Developer',\n 'user.professional.employer': 'Innofluence'\n };\n var transformed = dottie.transform(values);\n\n transforms is now equal to =\n {\n user: {\n name: 'Mick Hansen',\n email: 'mh@innofluence.com',\n professional: {\n title: 'Developer',\n employer: 'Innofluence'\n }\n }\n }", "readmeFilename": "README.md", "bugs": { "url": "https://github.com/greenwoodio/dottie.js/issues" }, "_id": "dottie@0.0.9-0", "dist": { "shasum": "ba7717ebf04cf5135d7a7c3e6eeb16b2b87b90dd" }, "_from": "dottie@", "_resolved": "https://registry.npmjs.org/dottie/-/dottie-0.0.9-0.tgz" } jju-1.1.0/index.js000066400000000000000000000014001240215127700137420ustar00rootroot00000000000000 module.exports.__defineGetter__('parse', function() { return require('./lib/parse').parse }) module.exports.__defineGetter__('stringify', function() { return require('./lib/stringify').stringify }) module.exports.__defineGetter__('tokenize', function() { return require('./lib/parse').tokenize }) module.exports.__defineGetter__('update', function() { return require('./lib/document').update }) module.exports.__defineGetter__('analyze', function() { return require('./lib/analyze').analyze }) module.exports.__defineGetter__('utils', function() { return require('./lib/utils') }) /**package { "name": "jju", "version": "0.0.0", "dependencies": {"js-yaml": "*"}, "scripts": {"postinstall": "js-yaml package.yaml > package.json ; npm install"} } **/ jju-1.1.0/lib/000077500000000000000000000000001240215127700130505ustar00rootroot00000000000000jju-1.1.0/lib/analyze.js000066400000000000000000000045721240215127700150610ustar00rootroot00000000000000/* * Author: Alex Kocharin * GIT: https://github.com/rlidwka/jju * License: WTFPL, grab your copy here: http://www.wtfpl.net/txt/copying/ */ var assert = require('assert') , tokenize = require('./parse').tokenize module.exports.analyze = function analyzeJSON(input, options) { if (options == null) options = {} if (!Array.isArray(input)) { input = tokenize(input, options) } var result = { has_whitespace: false, has_comments: false, has_newlines: false, has_trailing_comma: false, indent: '', newline: '\n', quote: '"', quote_keys: true, } var stats = { indent: {}, newline: {}, quote: {}, } for (var i=0; i stats[k][b] ? a : b }) } } return result } jju-1.1.0/lib/document.js000066400000000000000000000323561240215127700152350ustar00rootroot00000000000000/* * Author: Alex Kocharin * GIT: https://github.com/rlidwka/jju * License: WTFPL, grab your copy here: http://www.wtfpl.net/txt/copying/ */ var assert = require('assert') , tokenize = require('./parse').tokenize , stringify = require('./stringify').stringify , analyze = require('./analyze').analyze function isObject(x) { return typeof(x) === 'object' && x !== null } function value_to_tokenlist(value, stack, options, is_key, indent) { options = Object.create(options) options._stringify_key = !!is_key if (indent) { options._prefix = indent.prefix.map(function(x) { return x.raw }).join('') } if (options._splitMin == null) options._splitMin = 0 if (options._splitMax == null) options._splitMax = 0 var stringified = stringify(value, options) if (is_key) { return [ { raw: stringified, type: 'key', stack: stack, value: value } ] } options._addstack = stack var result = tokenize(stringified, { _addstack: stack, }) result.data = null return result } // '1.2.3' -> ['1','2','3'] function arg_to_path(path) { // array indexes if (typeof(path) === 'number') path = String(path) if (path === '') path = [] if (typeof(path) === 'string') path = path.split('.') if (!Array.isArray(path)) throw new Error('Invalid path type, string or array expected') return path } // returns new [begin, end] or false if not found // // {x:3, xxx: 111, y: [111, {q: 1, e: 2} ,333] } // f('y',0) returns this B^^^^^^^^^^^^^^^^^^^^^^^^E // then f('1',1) would reduce it to B^^^^^^^^^^E function find_element_in_tokenlist(element, lvl, tokens, begin, end) { while(tokens[begin].stack[lvl] != element) { if (begin++ >= end) return false } while(tokens[end].stack[lvl] != element) { if (end-- < begin) return false } return [begin, end] } function is_whitespace(token_type) { return token_type === 'whitespace' || token_type === 'newline' || token_type === 'comment' } function find_first_non_ws_token(tokens, begin, end) { while(is_whitespace(tokens[begin].type)) { if (begin++ >= end) return false } return begin } function find_last_non_ws_token(tokens, begin, end) { while(is_whitespace(tokens[end].type)) { if (end-- < begin) return false } return end } /* * when appending a new element of an object/array, we are trying to * figure out the style used on the previous element * * return {prefix, sep1, sep2, suffix} * * ' "key" : "element" \r\n' * prefix^^^^ sep1^ ^^sep2 ^^^^^^^^suffix * * begin - the beginning of the object/array * end - last token of the last element (value or comma usually) */ function detect_indent_style(tokens, is_array, begin, end, level) { var result = { sep1: [], sep2: [], suffix: [], prefix: [], newline: [], } if (tokens[end].type === 'separator' && tokens[end].stack.length !== level+1 && tokens[end].raw !== ',') { // either a beginning of the array (no last element) or other weird situation // // just return defaults return result } // ' "key" : "value" ,' // skipping last separator, we're now here ^^ if (tokens[end].type === 'separator') end = find_last_non_ws_token(tokens, begin, end - 1) if (end === false) return result // ' "key" : "value" ,' // skipping value ^^^^^^^ while(tokens[end].stack.length > level) end-- if (!is_array) { while(is_whitespace(tokens[end].type)) { if (end < begin) return result if (tokens[end].type === 'whitespace') { result.sep2.unshift(tokens[end]) } else { // newline, comment or other unrecognized codestyle return result } end-- } // ' "key" : "value" ,' // skipping separator ^ assert.equal(tokens[end].type, 'separator') assert.equal(tokens[end].raw, ':') while(is_whitespace(tokens[--end].type)) { if (end < begin) return result if (tokens[end].type === 'whitespace') { result.sep1.unshift(tokens[end]) } else { // newline, comment or other unrecognized codestyle return result } } assert.equal(tokens[end].type, 'key') end-- } // ' "key" : "value" ,' // skipping key ^^^^^ while(is_whitespace(tokens[end].type)) { if (end < begin) return result if (tokens[end].type === 'whitespace') { result.prefix.unshift(tokens[end]) } else if (tokens[end].type === 'newline') { result.newline.unshift(tokens[end]) return result } else { // comment or other unrecognized codestyle return result } end-- } return result } function Document(text, options) { if (!(this instanceof Document)) return new Document(text, options) if (options == null) options = {} //options._structure = true var tokens = this._tokens = tokenize(text, options) this._data = tokens.data tokens.data = null this._options = options var stats = analyze(text, options) if (options.indent == null) { options.indent = stats.indent } if (options.quote == null) { options.quote = stats.quote } if (options.quote_keys == null) { options.quote_keys = stats.quote_keys } if (options.no_trailing_comma == null) { options.no_trailing_comma = !stats.has_trailing_comma } } // return true if it's a proper object // throw otherwise function check_if_can_be_placed(key, object, is_unset) { //if (object == null) return false function error(add) { return new Error("You can't " + (is_unset ? 'unset' : 'set') + " key '" + key + "'" + add) } if (!isObject(object)) { throw error(' of an non-object') } if (Array.isArray(object)) { // array, check boundary if (String(key).match(/^\d+$/)) { key = Number(String(key)) if (object.length < key || (is_unset && object.length === key)) { throw error(', out of bounds') } else if (is_unset && object.length !== key+1) { throw error(' in the middle of an array') } else { return true } } else { throw error(' of an array') } } else { // object return true } } // usage: document.set('path.to.something', 'value') // or: document.set(['path','to','something'], 'value') Document.prototype.set = function(path, value) { path = arg_to_path(path) // updating this._data and check for errors if (path.length === 0) { if (value === undefined) throw new Error("can't remove root document") this._data = value var new_key = false } else { var data = this._data for (var i=0; i {x:1}` // removing sep, literal and optional sep // ':' var pos2 = find_last_non_ws_token(this._tokens, pos_old[0], position[0] - 1) assert.equal(this._tokens[pos2].type, 'separator') assert.equal(this._tokens[pos2].raw, ':') position[0] = pos2 // key var pos2 = find_last_non_ws_token(this._tokens, pos_old[0], position[0] - 1) assert.equal(this._tokens[pos2].type, 'key') assert.equal(this._tokens[pos2].value, path[path.length-1]) position[0] = pos2 } // removing comma in arrays and objects var pos2 = find_last_non_ws_token(this._tokens, pos_old[0], position[0] - 1) assert.equal(this._tokens[pos2].type, 'separator') if (this._tokens[pos2].raw === ',') { position[0] = pos2 } else { // beginning of the array/object, so we should remove trailing comma instead pos2 = find_first_non_ws_token(this._tokens, position[1] + 1, pos_old[1]) assert.equal(this._tokens[pos2].type, 'separator') if (this._tokens[pos2].raw === ',') { position[1] = pos2 } } } else { var indent = pos2 !== false ? detect_indent_style(this._tokens, Array.isArray(data), pos_old[0], position[1] - 1, i) : {} var newtokens = value_to_tokenlist(value, path, this._options, false, indent) } } else { // insert new key, that's tricky var path_1 = path.slice(0, i) // find a last separator after which we're inserting it var pos2 = find_last_non_ws_token(this._tokens, position[0] + 1, position[1] - 1) assert(pos2 !== false) var indent = pos2 !== false ? detect_indent_style(this._tokens, Array.isArray(data), position[0] + 1, pos2, i) : {} var newtokens = value_to_tokenlist(value, path, this._options, false, indent) // adding leading whitespaces according to detected codestyle var prefix = [] if (indent.newline && indent.newline.length) prefix = prefix.concat(indent.newline) if (indent.prefix && indent.prefix.length) prefix = prefix.concat(indent.prefix) // adding '"key":' (as in "key":"value") to object values if (!Array.isArray(data)) { prefix = prefix.concat(value_to_tokenlist(path[path.length-1], path_1, this._options, true)) if (indent.sep1 && indent.sep1.length) prefix = prefix.concat(indent.sep1) prefix.push({raw: ':', type: 'separator', stack: path_1}) if (indent.sep2 && indent.sep2.length) prefix = prefix.concat(indent.sep2) } newtokens.unshift.apply(newtokens, prefix) // check if prev token is a separator AND they're at the same level if (this._tokens[pos2].type === 'separator' && this._tokens[pos2].stack.length === path.length-1) { // previous token is either , or [ or { if (this._tokens[pos2].raw === ',') { // restore ending comma newtokens.push({raw: ',', type: 'separator', stack: path_1}) } } else { // previous token isn't a separator, so need to insert one newtokens.unshift({raw: ',', type: 'separator', stack: path_1}) } if (indent.suffix && indent.suffix.length) newtokens.push.apply(newtokens, indent.suffix) assert.equal(this._tokens[position[1]].type, 'separator') position[0] = pos2+1 position[1] = pos2 } newtokens.unshift(position[1] - position[0] + 1) newtokens.unshift(position[0]) this._tokens.splice.apply(this._tokens, newtokens) return this } // convenience method Document.prototype.unset = function(path) { return this.set(path, undefined) } Document.prototype.get = function(path) { path = arg_to_path(path) var data = this._data for (var i=0; i old_data.length) { // adding new elements, so going forward for (var i=0; i=0; i--) { path.push(String(i)) change(path, old_data[i], new_data[i]) path.pop() } } } else { // both values are objects here for (var i in new_data) { path.push(String(i)) change(path, old_data[i], new_data[i]) path.pop() } for (var i in old_data) { if (i in new_data) continue path.push(String(i)) change(path, old_data[i], new_data[i]) path.pop() } } } } Document.prototype.toString = function() { return this._tokens.map(function(x) { return x.raw }).join('') } module.exports.Document = Document module.exports.update = function updateJSON(source, new_value, options) { return (new Document(source, options)).update(new_value).toString() } jju-1.1.0/lib/parse.js000066400000000000000000000404531240215127700145260ustar00rootroot00000000000000/* * Author: Alex Kocharin * GIT: https://github.com/rlidwka/jju * License: WTFPL, grab your copy here: http://www.wtfpl.net/txt/copying/ */ // RTFM: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf var Uni = require('./unicode') function isHexDigit(x) { return (x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f') } function isOctDigit(x) { return x >= '0' && x <= '7' } function isDecDigit(x) { return x >= '0' && x <= '9' } var unescapeMap = { '\'': '\'', '"' : '"', '\\': '\\', 'b' : '\b', 'f' : '\f', 'n' : '\n', 'r' : '\r', 't' : '\t', 'v' : '\v', '/' : '/', } function formatError(input, msg, position, lineno, column, json5) { var result = msg + ' at ' + (lineno + 1) + ':' + (column + 1) , tmppos = position - column - 1 , srcline = '' , underline = '' var isLineTerminator = json5 ? Uni.isLineTerminator : Uni.isLineTerminatorJSON // output no more than 70 characters before the wrong ones if (tmppos < position - 70) { tmppos = position - 70 } while (1) { var chr = input[++tmppos] if (isLineTerminator(chr) || tmppos === input.length) { if (position >= tmppos) { // ending line error, so show it after the last char underline += '^' } break } srcline += chr if (position === tmppos) { underline += '^' } else if (position > tmppos) { underline += input[tmppos] === '\t' ? '\t' : ' ' } // output no more than 78 characters on the string if (srcline.length > 78) break } return result + '\n' + srcline + '\n' + underline } function parse(input, options) { // parse as a standard JSON mode var json5 = !(options.mode === 'json' || options.legacy) var isLineTerminator = json5 ? Uni.isLineTerminator : Uni.isLineTerminatorJSON var isWhiteSpace = json5 ? Uni.isWhiteSpace : Uni.isWhiteSpaceJSON var length = input.length , lineno = 0 , linestart = 0 , position = 0 , stack = [] var tokenStart = function() {} var tokenEnd = function(v) {return v} /* tokenize({ raw: '...', type: 'whitespace'|'comment'|'key'|'literal'|'separator'|'newline', value: 'number'|'string'|'whatever', path: [...], }) */ if (options._tokenize) { ;(function() { var start = null tokenStart = function() { if (start !== null) throw new Error('internal error, token overlap') start = position } tokenEnd = function(v, type) { if (start != position) { var hash = { raw: input.substr(start, position-start), type: type, stack: stack.slice(0), } if (v !== undefined) hash.value = v options._tokenize.call(null, hash) } start = null return v } })() } function fail(msg) { var column = position - linestart if (!msg) { if (position < length) { var token = '\'' + JSON .stringify(input[position]) .replace(/^"|"$/g, '') .replace(/'/g, "\\'") .replace(/\\"/g, '"') + '\'' if (!msg) msg = 'Unexpected token ' + token } else { if (!msg) msg = 'Unexpected end of input' } } var error = new SyntaxError(formatError(input, msg, position, lineno, column, json5)) error.row = lineno + 1 error.column = column + 1 throw error } function newline(chr) { // account for if (chr === '\r' && input[position] === '\n') position++ linestart = position lineno++ } function parseGeneric() { var result while (position < length) { tokenStart() var chr = input[position++] if (chr === '"' || (chr === '\'' && json5)) { return tokenEnd(parseString(chr), 'literal') } else if (chr === '{') { tokenEnd(undefined, 'separator') return parseObject() } else if (chr === '[') { tokenEnd(undefined, 'separator') return parseArray() } else if (chr === '-' || chr === '.' || isDecDigit(chr) // + number Infinity NaN || (json5 && (chr === '+' || chr === 'I' || chr === 'N')) ) { return tokenEnd(parseNumber(), 'literal') } else if (chr === 'n') { parseKeyword('null') return tokenEnd(null, 'literal') } else if (chr === 't') { parseKeyword('true') return tokenEnd(true, 'literal') } else if (chr === 'f') { parseKeyword('false') return tokenEnd(false, 'literal') } else { position-- return tokenEnd(undefined) } } } function parseKey() { var result while (position < length) { tokenStart() var chr = input[position++] if (chr === '"' || (chr === '\'' && json5)) { return tokenEnd(parseString(chr), 'key') } else if (chr === '{') { tokenEnd(undefined, 'separator') return parseObject() } else if (chr === '[') { tokenEnd(undefined, 'separator') return parseArray() } else if (chr === '.' || isDecDigit(chr) ) { return tokenEnd(parseNumber(true), 'key') } else if (json5 && Uni.isIdentifierStart(chr) || (chr === '\\' && input[position] === 'u')) { // unicode char or a unicode sequence var rollback = position - 1 var result = parseIdentifier() if (result === undefined) { position = rollback return tokenEnd(undefined) } else { return tokenEnd(result, 'key') } } else { position-- return tokenEnd(undefined) } } } function skipWhiteSpace() { tokenStart() while (position < length) { var chr = input[position++] if (isLineTerminator(chr)) { position-- tokenEnd(undefined, 'whitespace') tokenStart() position++ newline(chr) tokenEnd(undefined, 'newline') tokenStart() } else if (isWhiteSpace(chr)) { // nothing } else if (chr === '/' && json5 && (input[position] === '/' || input[position] === '*') ) { position-- tokenEnd(undefined, 'whitespace') tokenStart() position++ skipComment(input[position++] === '*') tokenEnd(undefined, 'comment') tokenStart() } else { position-- break } } return tokenEnd(undefined, 'whitespace') } function skipComment(multi) { while (position < length) { var chr = input[position++] if (isLineTerminator(chr)) { // LineTerminator is an end of singleline comment if (!multi) { // let parent function deal with newline position-- return } newline(chr) } else if (chr === '*' && multi) { // end of multiline comment if (input[position] === '/') { position++ return } } else { // nothing } } if (multi) { fail('Unclosed multiline comment') } } function parseKeyword(keyword) { // keyword[0] is not checked because it should've checked earlier var _pos = position var len = keyword.length for (var i=1; i= length || keyword[i] != input[position]) { position = _pos-1 fail() } position++ } } function parseObject() { var result = options.null_prototype ? Object.create(null) : {} , empty_object = {} , is_non_empty = false while (position < length) { skipWhiteSpace() var item1 = parseKey() skipWhiteSpace() tokenStart() var chr = input[position++] tokenEnd(undefined, 'separator') if (chr === '}' && item1 === undefined) { if (!json5 && is_non_empty) { position-- fail('Trailing comma in object') } return result } else if (chr === ':' && item1 !== undefined) { skipWhiteSpace() stack.push(item1) var item2 = parseGeneric() stack.pop() if (item2 === undefined) fail('No value found for key ' + item1) if (typeof(item1) !== 'string') { if (!json5 || typeof(item1) !== 'number') { fail('Wrong key type: ' + item1) } } if ((item1 in empty_object || empty_object[item1] != null) && options.reserved_keys !== 'replace') { if (options.reserved_keys === 'throw') { fail('Reserved key: ' + item1) } else { // silently ignore it } } else { if (typeof(options.reviver) === 'function') { item2 = options.reviver.call(null, item1, item2) } if (item2 !== undefined) { is_non_empty = true Object.defineProperty(result, item1, { value: item2, enumerable: true, configurable: true, writable: true, }) } } skipWhiteSpace() tokenStart() var chr = input[position++] tokenEnd(undefined, 'separator') if (chr === ',') { continue } else if (chr === '}') { return result } else { fail() } } else { position-- fail() } } fail() } function parseArray() { var result = [] while (position < length) { skipWhiteSpace() stack.push(result.length) var item = parseGeneric() stack.pop() skipWhiteSpace() tokenStart() var chr = input[position++] tokenEnd(undefined, 'separator') if (item !== undefined) { if (typeof(options.reviver) === 'function') { item = options.reviver.call(null, String(result.length), item) } if (item === undefined) { result.length++ item = true // hack for check below, not included into result } else { result.push(item) } } if (chr === ',') { if (item === undefined) { fail('Elisions are not supported') } } else if (chr === ']') { if (!json5 && item === undefined && result.length) { position-- fail('Trailing comma in array') } return result } else { position-- fail() } } } function parseNumber() { // rewind because we don't know first char position-- var start = position , chr = input[position++] , t var to_num = function(is_octal) { var str = input.substr(start, position - start) if (is_octal) { var result = parseInt(str.replace(/^0o?/, ''), 8) } else { var result = Number(str) } if (Number.isNaN(result)) { position-- fail('Bad numeric literal - "' + input.substr(start, position - start + 1) + '"') } else if (!json5 && !str.match(/^-?(0|[1-9][0-9]*)(\.[0-9]+)?(e[+-]?[0-9]+)?$/i)) { // additional restrictions imposed by json position-- fail('Non-json numeric literal - "' + input.substr(start, position - start + 1) + '"') } else { return result } } // ex: -5982475.249875e+29384 // ^ skipping this if (chr === '-' || (chr === '+' && json5)) chr = input[position++] if (chr === 'N' && json5) { parseKeyword('NaN') return NaN } if (chr === 'I' && json5) { parseKeyword('Infinity') // returning +inf or -inf return to_num() } if (chr >= '1' && chr <= '9') { // ex: -5982475.249875e+29384 // ^^^ skipping these while (position < length && isDecDigit(input[position])) position++ chr = input[position++] } // special case for leading zero: 0.123456 if (chr === '0') { chr = input[position++] // new syntax, "0o777" old syntax, "0777" var is_octal = chr === 'o' || chr === 'O' || isOctDigit(chr) var is_hex = chr === 'x' || chr === 'X' if (json5 && (is_octal || is_hex)) { while (position < length && (is_hex ? isHexDigit : isOctDigit)( input[position] ) ) position++ var sign = 1 if (input[start] === '-') { sign = -1 start++ } else if (input[start] === '+') { start++ } return sign * to_num(is_octal) } } if (chr === '.') { // ex: -5982475.249875e+29384 // ^^^ skipping these while (position < length && isDecDigit(input[position])) position++ chr = input[position++] } if (chr === 'e' || chr === 'E') { chr = input[position++] if (chr === '-' || chr === '+') position++ // ex: -5982475.249875e+29384 // ^^^ skipping these while (position < length && isDecDigit(input[position])) position++ chr = input[position++] } // we have char in the buffer, so count for it position-- return to_num() } function parseIdentifier() { // rewind because we don't know first char position-- var result = '' while (position < length) { var chr = input[position++] if (chr === '\\' && input[position] === 'u' && isHexDigit(input[position+1]) && isHexDigit(input[position+2]) && isHexDigit(input[position+3]) && isHexDigit(input[position+4]) ) { // UnicodeEscapeSequence chr = String.fromCharCode(parseInt(input.substr(position+1, 4), 16)) position += 5 } if (result.length) { // identifier started if (Uni.isIdentifierPart(chr)) { result += chr } else { position-- return result } } else { if (Uni.isIdentifierStart(chr)) { result += chr } else { return undefined } } } fail() } function parseString(endChar) { // 7.8.4 of ES262 spec var result = '' while (position < length) { var chr = input[position++] if (chr === endChar) { return result } else if (chr === '\\') { if (position >= length) fail() chr = input[position++] if (unescapeMap[chr] && (json5 || (chr != 'v' && chr != "'"))) { result += unescapeMap[chr] } else if (json5 && isLineTerminator(chr)) { // line continuation newline(chr) } else if (chr === 'u' || (chr === 'x' && json5)) { // unicode/character escape sequence var off = chr === 'u' ? 4 : 2 // validation for \uXXXX for (var i=0; i= length) fail() if (!isHexDigit(input[position])) fail('Bad escape sequence') position++ } result += String.fromCharCode(parseInt(input.substr(position-off, off), 16)) } else if (json5 && isOctDigit(chr)) { if (chr < '4' && isOctDigit(input[position]) && isOctDigit(input[position+1])) { // three-digit octal var digits = 3 } else if (isOctDigit(input[position])) { // two-digit octal var digits = 2 } else { var digits = 1 } position += digits - 1 result += String.fromCharCode(parseInt(input.substr(position-digits, digits), 8)) /*if (!isOctDigit(input[position])) { // \0 is allowed still result += '\0' } else { fail('Octal literals are not supported') }*/ } else if (json5) { // \X -> x result += chr } else { position-- fail() } } else if (isLineTerminator(chr)) { fail() } else { if (!json5 && chr.charCodeAt(0) < 32) { position-- fail('Unexpected control character') } // SourceCharacter but not one of " or \ or LineTerminator result += chr } } fail() } skipWhiteSpace() var return_value = parseGeneric() if (return_value !== undefined || position < length) { skipWhiteSpace() if (position >= length) { if (typeof(options.reviver) === 'function') { return_value = options.reviver.call(null, '', return_value) } return return_value } else { fail() } } else { if (position) { fail('No data, only a whitespace') } else { fail('No data, empty input') } } } /* * parse(text, options) * or * parse(text, reviver) * * where: * text - string * options - object * reviver - function */ module.exports.parse = function parseJSON(input, options) { // support legacy functions if (typeof(options) === 'function') { options = { reviver: options } } if (input === undefined) { // parse(stringify(x)) should be equal x // with JSON functions it is not 'cause of undefined // so we're fixing it return undefined } // JSON.parse compat if (typeof(input) !== 'string') input = String(input) if (options == null) options = {} if (options.reserved_keys == null) options.reserved_keys = 'ignore' if (options.reserved_keys === 'throw' || options.reserved_keys === 'ignore') { if (options.null_prototype == null) { options.null_prototype = true } } try { return parse(input, options) } catch(err) { // jju is a recursive parser, so JSON.parse("{{{{{{{") could blow up the stack // // this catch is used to skip all those internal calls if (err instanceof SyntaxError && err.row != null && err.column != null) { var old_err = err err = new SyntaxError(old_err.message) err.column = old_err.column err.row = old_err.row } throw err } } module.exports.tokenize = function tokenizeJSON(input, options) { if (options == null) options = {} options._tokenize = function(smth) { if (options._addstack) smth.stack.unshift.apply(smth.stack, options._addstack) tokens.push(smth) } var tokens = [] tokens.data = module.exports.parse(input, options) return tokens } jju-1.1.0/lib/stringify.js000066400000000000000000000237471240215127700154410ustar00rootroot00000000000000/* * Author: Alex Kocharin * GIT: https://github.com/rlidwka/jju * License: WTFPL, grab your copy here: http://www.wtfpl.net/txt/copying/ */ var Uni = require('./unicode') // Fix Function#name on browsers that do not support it (IE) // http://stackoverflow.com/questions/6903762/function-name-not-supported-in-ie if (!(function f(){}).name) { Object.defineProperty((function(){}).constructor.prototype, 'name', { get: function() { var name = this.toString().match(/^\s*function\s*(\S*)\s*\(/)[1] // For better performance only parse once, and then cache the // result through a new accessor for repeated access. Object.defineProperty(this, 'name', { value: name }) return name } }) } var special_chars = { 0: '\\0', // this is not an octal literal 8: '\\b', 9: '\\t', 10: '\\n', 11: '\\v', 12: '\\f', 13: '\\r', 92: '\\\\', } // for oddballs var hasOwnProperty = Object.prototype.hasOwnProperty // some people escape those, so I'd copy this to be safe var escapable = /[\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/ function _stringify(object, options, recursiveLvl, currentKey) { var opt_json = options.mode === 'json' /* * Opinionated decision warning: * * Objects are serialized in the following form: * { type: 'Class', data: DATA } * * Class is supposed to be a function, and new Class(DATA) is * supposed to be equivalent to the original value */ /*function custom_type() { return stringify({ type: object.constructor.name, data: object.toString() }) }*/ // if add, it's an internal indentation, so we add 1 level and a eol // if !add, it's an ending indentation, so we just indent function indent(str, add) { var prefix = options._prefix ? options._prefix : '' if (!options.indent) return prefix + str var result = '' var count = recursiveLvl + (add || 0) for (var i=0; i 0) { if (!Uni.isIdentifierPart(key[i])) return _stringify_str(key) } else { if (!Uni.isIdentifierStart(key[i])) return _stringify_str(key) } var chr = key.charCodeAt(i) if (options.ascii) { if (chr < 0x80) { result += key[i] } else { result += '\\u' + ('0000' + chr.toString(16)).slice(-4) } } else { if (escapable.exec(key[i])) { result += '\\u' + ('0000' + chr.toString(16)).slice(-4) } else { result += key[i] } } } return result } function _stringify_str(key) { var quote = options.quote var quoteChr = quote.charCodeAt(0) var result = '' for (var i=0; i= 8 && chr <= 13 && (!opt_json || chr !== 11)) { result += special_chars[chr] } else if (!opt_json) { result += '\\x0' + chr.toString(16) } else { result += '\\u000' + chr.toString(16) } } else if (chr < 0x20) { if (!opt_json) { result += '\\x' + chr.toString(16) } else { result += '\\u00' + chr.toString(16) } } else if (chr >= 0x20 && chr < 0x80) { // ascii range if (chr === 47 && i && key[i-1] === '<') { // escaping slashes in result += '\\' + key[i] } else if (chr === 92) { result += '\\\\' } else if (chr === quoteChr) { result += '\\' + quote } else { result += key[i] } } else if (options.ascii || Uni.isLineTerminator(key[i]) || escapable.exec(key[i])) { if (chr < 0x100) { if (!opt_json) { result += '\\x' + chr.toString(16) } else { result += '\\u00' + chr.toString(16) } } else if (chr < 0x1000) { result += '\\u0' + chr.toString(16) } else if (chr < 0x10000) { result += '\\u' + chr.toString(16) } else { throw new Error('weird codepoint') } } else { result += key[i] } } return quote + result + quote } function _stringify_object() { if (object === null) return 'null' var result = [] , len = 0 , braces if (Array.isArray(object)) { braces = '[]' for (var i=0; i options._splitMax - recursiveLvl * options.indent.length || len > options._splitMin) ) { // remove trailing comma in multiline if asked to if (options.no_trailing_comma && result.length) { result[result.length-1] = result[result.length-1].substring(0, result[result.length-1].length-1) } var innerStuff = result.map(function(x) {return indent(x, 1)}).join('') return braces[0] + (options.indent ? '\n' : '') + innerStuff + indent(braces[1]) } else { // always remove trailing comma in one-lined arrays if (result.length) { result[result.length-1] = result[result.length-1].substring(0, result[result.length-1].length-1) } var innerStuff = result.join(options.indent ? ' ' : '') return braces[0] + innerStuff + braces[1] } } function _stringify_nonobject(object) { if (typeof(options.replacer) === 'function') { object = options.replacer.call(null, currentKey, object) } switch(typeof(object)) { case 'string': return _stringify_str(object) case 'number': if (object === 0 && 1/object < 0) { // Opinionated decision warning: // // I want cross-platform negative zero in all js engines // I know they're equal, but why lose that tiny bit of // information needlessly? return '-0' } if (options.mode === 'json' && !Number.isFinite(object)) { // json don't support infinity (= sucks) return 'null' } return object.toString() case 'boolean': return object.toString() case 'undefined': return undefined case 'function': // return custom_type() default: // fallback for something weird return JSON.stringify(object) } } if (options._stringify_key) { return _stringify_key(object) } if (typeof(object) === 'object') { if (object === null) return 'null' var str if (typeof(str = object.toJSON5) === 'function' && options.mode !== 'json') { object = str.call(object, currentKey) } else if (typeof(str = object.toJSON) === 'function') { object = str.call(object, currentKey) } if (object === null) return 'null' if (typeof(object) !== 'object') return _stringify_nonobject(object) if (object.constructor === Number || object.constructor === Boolean || object.constructor === String) { object = object.valueOf() return _stringify_nonobject(object) } else if (object.constructor === Date) { // only until we can't do better return _stringify_nonobject(object.toISOString()) } else { if (typeof(options.replacer) === 'function') { object = options.replacer.call(null, currentKey, object) if (typeof(object) !== 'object') return _stringify_nonobject(object) } return _stringify_object(object) } } else { return _stringify_nonobject(object) } } /* * stringify(value, options) * or * stringify(value, replacer, space) * * where: * value - anything * options - object * replacer - function or array * space - boolean or number or string */ module.exports.stringify = function stringifyJSON(object, options, _space) { // support legacy syntax if (typeof(options) === 'function' || Array.isArray(options)) { options = { replacer: options } } else if (typeof(options) === 'object' && options !== null) { // nothing to do } else { options = {} } if (_space != null) options.indent = _space if (options.indent == null) options.indent = '\t' if (options.quote == null) options.quote = "'" if (options.ascii == null) options.ascii = false if (options.mode == null) options.mode = 'simple' if (options.mode === 'json') { // json only supports double quotes (= sucks) options.quote = '"' // json don't support trailing commas (= sucks) options.no_trailing_comma = true // json don't support unquoted property names (= sucks) options.quote_keys = true } // why would anyone use such objects? if (typeof(options.indent) === 'object') { if (options.indent.constructor === Number || options.indent.constructor === Boolean || options.indent.constructor === String) options.indent = options.indent.valueOf() } // gap is capped at 10 characters if (typeof(options.indent) === 'number') { if (options.indent >= 0) { options.indent = Array(Math.min(~~options.indent, 10) + 1).join(' ') } else { options.indent = false } } else if (typeof(options.indent) === 'string') { options.indent = options.indent.substr(0, 10) } if (options._splitMin == null) options._splitMin = 50 if (options._splitMax == null) options._splitMax = 70 return _stringify(object, options, 0, '') } jju-1.1.0/lib/unicode.js000066400000000000000000000260731240215127700150440ustar00rootroot00000000000000 // This is autogenerated with esprima tools, see: // https://github.com/ariya/esprima/blob/master/esprima.js // // PS: oh God, I hate Unicode // ECMAScript 5.1/Unicode v6.3.0 NonAsciiIdentifierStart: var Uni = module.exports module.exports.isWhiteSpace = function isWhiteSpace(x) { // section 7.2, table 2 return x === '\u0020' || x === '\u00A0' || x === '\uFEFF' // <-- this is not a Unicode WS, only a JS one // + 9 B C below // + whitespace characters from unicode 6.0.0 // http://www.unicode.org/versions/Unicode6.0.0/ch04.pdf || (x >= '\u0009' && x <= '\u000D') // 9 A B C D || x === '\u0085' || x === '\u1680' || x === '\u180E' || (x >= '\u2000' && x <= '\u200A') // 0 1 2 3 4 5 6 7 8 9 A || x === '\u2028' || x === '\u2029' || x === '\u202F' || x === '\u205F' || x === '\u3000' // should be total of 26+1 = 27 characters } module.exports.isWhiteSpaceJSON = function isWhiteSpaceJSON(x) { return x === '\u0020' || x === '\u0009' || x === '\u000A' || x === '\u000D' } module.exports.isLineTerminator = function isLineTerminator(x) { // ok, here is the part when JSON is wrong // section 7.3, table 3 return x === '\u000A' || x === '\u000D' || x === '\u2028' || x === '\u2029' } module.exports.isLineTerminatorJSON = function isLineTerminatorJSON(x) { return x === '\u000A' || x === '\u000D' } module.exports.isIdentifierStart = function isIdentifierStart(x) { return x === '$' || x === '_' || (x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') || (x >= '\u0080' && Uni.NonAsciiIdentifierStart.test(x)) } module.exports.isIdentifierPart = function isIdentifierPart(x) { return x === '$' || x === '_' || (x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') || (x >= '0' && x <= '9') // <-- addition to Start || (x >= '\u0080' && Uni.NonAsciiIdentifierPart.test(x)) } module.exports.NonAsciiIdentifierStart = /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]/ // ECMAScript 5.1/Unicode v6.3.0 NonAsciiIdentifierPart: module.exports.NonAsciiIdentifierPart = /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0\u08A2-\u08AC\u08E4-\u08FE\u0900-\u0963\u0966-\u096F\u0971-\u0977\u0979-\u097F\u0981-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58\u0C59\u0C60-\u0C63\u0C66-\u0C6F\u0C82\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D02\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D60-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191C\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1D00-\u1DE6\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA697\uA69F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A\uAA7B\uAA80-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE26\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]/ jju-1.1.0/lib/utils.js000066400000000000000000000022631240215127700145510ustar00rootroot00000000000000var jju = require('../') , fs = require('fs') // this function registers json5 extension, so you // can do `require("./config.json5")` kind of thing module.exports.register = function() { var r = require, e = 'extensions' r[e]['.json5'] = function(m, f) { /*eslint no-sync:0*/ m.exports = jju.parse(fs.readFileSync(f, 'utf8')) } } // this function monkey-patches JSON.parse, so it // will return an exact position of error in case // of parse failure module.exports.patch_JSON_parse = function() { var _parse = JSON.parse JSON.parse = function(text, rev) { try { return _parse(text, rev) } catch(err) { // this call should always throw require('jju').parse(text, { mode: 'json', legacy: true, reviver: rev, reserved_keys: 'replace', null_prototype: false, }) // if it didn't throw, but original parser did, // this is an error in this library and should be reported throw err } } } // this function is an express/connect middleware // that accepts uploads in application/json5 format module.exports.middleware = function() { return function(req, res, next) { throw new Error('this function is removed, use express-json5 instead') } } jju-1.1.0/package.yaml000066400000000000000000000014061240215127700145620ustar00rootroot00000000000000# use "yapm install ." if you're installing this from git repository # "jju" stands for "json/json5 utils" name: jju version: 1.1.0 description: a set of utilities to work with JSON / JSON5 documents author: name: Alex Kocharin email: alex@kocharin.ru repository: type: git url: git://github.com/rlidwka/jju bugs: url: https://github.com/rlidwka/jju/issues homepage: http://rlidwka.github.io/jju/ devDependencies: mocha: '>=1.21.0' js-yaml: '>=3.1.0' # linting tools eslint: '~0.4.2' scripts: test: 'mocha test/*.js' lint: 'eslint -c ./.eslint.yaml ./lib' keywords: - json - json5 - parser - serializer - data publishConfig: registry: https://registry.npmjs.org/ license: type: WTFPL url: http://www.wtfpl.net/txt/copying/ jju-1.1.0/test/000077500000000000000000000000001240215127700132615ustar00rootroot00000000000000jju-1.1.0/test/external/000077500000000000000000000000001240215127700151035ustar00rootroot00000000000000jju-1.1.0/test/external/README.md000066400000000000000000000002631240215127700163630ustar00rootroot00000000000000v8.js - v8 json tests, concatentation of five files: - mjsunit.js - json.js - json2.js - json-parser-recursive.js - json-stringify-recursive.js json5-orig-\* - json5 tests jju-1.1.0/test/external/json5-orig-parse.js000066400000000000000000000020141240215127700205420ustar00rootroot00000000000000var assert = require('assert'); var FS = require('fs'); var Path = require('path'); var parse = require('../..').parse var dirsPath = Path.resolve(__dirname, 'parse-cases'); var dirs = FS.readdirSync(dirsPath); function addTest(arg) { // console.log('testing: ', arg) try { var x = parse(arg) } catch(err) { //console.log(err) x = 'fail' } try { var z = eval('(function(){"use strict"\nreturn ('+String(arg)+'\n)\n})()') } catch(err) { z = 'fail' } if (Number.isNaN(x)) x = '_NaN' if (Number.isNaN(z)) z = '_NaN' assert.deepEqual(x, z) } function createTest(fileName, dir) { var ext = Path.extname(fileName); var filePath = Path.join(dirsPath, dir, fileName); var str = FS.readFileSync(filePath, 'utf8'); addTest(str); } dirs.forEach(function (dir) { // create a test suite for this group of tests: exports[dir] = {}; // otherwise create a test for each file in this group: FS.readdirSync(Path.join(dirsPath, dir)).forEach(function (file) { createTest(file, dir); }); }); jju-1.1.0/test/external/json5-orig-str.js000066400000000000000000000107511240215127700202470ustar00rootroot00000000000000// tests stringify() /*global require console exports */ var JSON5 = {} JSON5.prototype = {toString: "[object JSON]"} JSON5.parse = function(x) { return require('../../').parse(x) } JSON5.stringify = function(x, _, indent) { return require('../../').stringify(x, {indent: indent || false, quote: '"'}) } process.nextTick(function() { for (var x in module.exports) { for (var y in module.exports[x]) { module.exports[x][y]() } } }); // set to true to show performance stats var DEBUG = false; var assert = require('assert'); // Test JSON5.stringify() by comparing its output for each case with // native JSON.stringify(). The only differences will be in how object keys are // handled. exports.stringify = {}; exports.stringify.simple = function test() { assertStringify(); assertStringify(null); assertStringify(9); assertStringify(-9); assertStringify(+9); assertStringify(+9.878); assertStringify(''); assertStringify("''"); assertStringify('999'); assertStringify('9aa'); assertStringify('aaa'); assertStringify('aa a'); assertStringify('aa\na'); assertStringify('aa\\a'); assertStringify('\''); assertStringify('\\\''); assertStringify('\\"'); assertStringify(undefined); assertStringify(true); assertStringify(false); assertStringify({}); assertStringify([]); assertStringify(function() {}); assertStringify(Date.now()); assertStringify(new Date(Date.now())); }; exports.stringify.oddities = function test() { assertStringify(Function); assertStringify(Date); assertStringify(Object); //assertStringify(NaN); //assertStringify(Infinity); assertStringify(10e6); assertStringify(19.3223e6); assertStringify(077); assertStringify(0x99); assertStringify(/aa/); assertStringify(new RegExp('aa')); assertStringify(new Number(7)); assertStringify(new String(7)); assertStringify(new String("")); assertStringify(new String("abcde")); assertStringify(new String(new String("abcde"))); assertStringify(new Boolean(true)); assertStringify(new Boolean()); }; exports.stringify.arrays = function test() { assertStringify([""]); assertStringify([1, 2]); assertStringify([undefined]); assertStringify([1, 'fasds']); assertStringify([1, '\n\b\t\f\r\'']); assertStringify([1, 'fasds', ['fdsafsd'], null]); assertStringify([1, 'fasds', ['fdsafsd'], null, function(aaa) { return 1; }, false ]); assertStringify([1, 'fasds', ['fdsafsd'], undefined, function(aaa) { return 1; }, false ]); }; exports.stringify.oddKeys = function test() { assertStringify({"this is a crazy long key":1}); assertStringify({"":1, '1bbbb':2}); assertStringify({"s\ns":1, '1bbbb':2}); assertStringify({'\n\b\t\f\r\'\\':1, '1bbbb':2}); }; function stringifyJSON5(obj, reviver, space) { var start, res, end; try { start = new Date(); res = JSON5.stringify(obj, null, space); end = new Date(); } catch (e) { res = e.message; end = new Date(); } if (DEBUG) { console.log('JSON5.stringify time: ' + (end-start)); console.log(res); } return res; } function stringifyJSON(obj, reviver, space) { var start, res, end; try { start = new Date(); res = JSON.stringify(obj, null, space); end = new Date(); // now remove all quotes from keys where appropriate // first recursively find all key names var keys = []; // now replace each key in the result var last = 0; for (var i = 0; i < keys.length; i++) { // not perfect since we can match on parts of the previous value that // matches the key, but we can design our test around that. last = res.indexOf('"' + keys[i] + '"', last); if (last === -1) { // problem with test framework console.log("Couldn't find: " + keys[i]); throw new Error("Couldn't find: " + keys[i]); } res = res.substring(0, last) + res.substring(last+1, last + keys[i].length+1) + res.substring(last + keys[i].length + 2, res.length); last += keys[i].length; } } catch (e) { res = e.message; end = new Date(); } if (DEBUG) { console.log('JSON.stringify time: ' + (end-start)); } return res; } function assertStringify(obj, expectError) { var j5, j; j5 = stringifyJSON5(obj); j = stringifyJSON(obj); assert.equal(j5, j); if (!expectError) { // no point in round tripping if there is an error var origStr = JSON5.stringify(obj), roundTripStr; if (origStr !== "undefined" && typeof origStr !== "undefined") { try { roundTripStr = JSON5.stringify(JSON5.parse(origStr)); } catch (e) { console.log(e); console.log(origStr); throw e; } assert.equal(origStr, roundTripStr); } } } jju-1.1.0/test/external/parse-cases.tgz000066400000000000000000000123161240215127700200420ustar00rootroot00000000000000FtR]ْ6 I%%pwg:rL~$H%.b?6."H)!p$H. ` VWnśW"p~jhicXo,IXQ޸>~:v])5 az0\e 8MZ]ӧt=m|Q֐YCA-H.4F!K&n[YG8 R9XytU|R/ ϰěMImwe#+^H.W,濿7dE{yu^{VML(}!__ (JR\چ3+YBΈ/=o~ XtWPMҘ?S"0aNq g'~nepo"Z!޻z0.9}5 *J9%w@_+s/r0JIOi@8n`Do !\ehFa ?v _X~ H~ hҝ&)}rj?etnߣy_h{ Rr6A[c%(^yYmwID +?JZmI 2?>*`+#''G:4y ύcY>fdCB-ob(Xj~N렇; $fkt@E-T8^כ_^ܽ,8!?7obpHz C]hH. ?%,VYSη_#DŽO! _$ݹX>K/pWzW1ڏ8/?NV@?  ~!s#pJt8?p q~# D%p{wݞܿC7Ռl_ +S")ϖ쾈Wr1>0U7McoLs~Ϛ O($}vnH>be$\cvO^_7}~@wrGo, ^fۤ 2VqV/ML2.\^W=\iH-yK|oJ'/Yz5䳪[reﱢh*]\gzR+-}Zg) ):M׻)0R*}^_~͗ۿpt|lhBX "]>Af3 ܆lehq#FBSa* hAJܜ4.x=aUgӍ䩈U]E"Y:DaQj]{>+헟]b_q-nLC<";1./7\+K?Ւ'o_w`O_1byYEB[NS[Rȳ=ia y>(yLn-Ǵ _ z ܣ5!1mP?|f&Ф'LgQ*_&;,Ф0DNx]r8Ip>GA [A, ZwoAP^CUE}Z' ዗'}/B)-TULj!.63qnOnGz?"Fuü|o?x?PDWMx3犪VT3$' jWuSh'(pPMI&Jї9@:9**p]y({Tr":21m4A9APJDW>C/Y)X=T6ޒ8 #.Jr"/KILU jMFj &Вxm7ېi 2<_]n-5!X$r(Җ7Jg)WtuI9Tȼk B|iURinmVUA+q{6T\KcR4iRK$IM/MrvGǾ: öa__ ,U3??B&^A8er?)h꿭?!AB0;П5 !h`}=F]_wcjOunf_!Gȉʞ:?,\8YEqA}rO9|'?Ym > F9,*ž`'?vSM7Mc#]>޺AϩW h3C%?=]Rߙћ_>K7[6!K@ѣK}ҟSGQ5"G0Wr1?Yѥ Q{2 ϸ)ҡcw}ϟcD_ ?uϧr&?M?Kq`m ȁB q4U]/^qѥߊL ASX_>t@Ga^eGӛb@;~7w'\]W86$"9Qw% }ykbdƋ0O?'2hM`R`8ZlaC_ f' /3H\okLYߨ>i6b@C8w !Pˋ$pϴ B0CɌ,`8 ` Rglԯ_ˍFP?q 'b0Yax0_ @__,$1z@Ot bk H ?FƉ߄_ T]# ` p׀ ?2c@D $H5C x9{ϑo6B`1dH?F{ @/i)Xyq?k0'V/??DXC /7Ʊ,.~#!ClY C8ا*;ko/UnoZkG7`!F!3߿S :?4_ol۰fl\?`Axn|@Z/U I{jju-1.1.0/test/external/v8.js.gz000066400000000000000000000216151240215127700164220ustar00rootroot00000000000000ıRv8.jssyPnsU42/0]OEp _+HK]I"ʕ?ɹp , >qaIG`,…lj88F4.G LtȏP"&~0/0tgKϿQs=;@ЎfdŔo(C&pGBLKM!"[xC*C} , ó`>Ħ#6nTwX<0:~L6zELy/ }Fz]J4y;pw7 |0wÃvEoZ'p4F4No FX Gb`o?Ǯo ߸p &9DPc ۞`@Iev`ܯcq {Hр72nޝ]>JJEg;S|I/Ā ( ;MjD>mBgQ* 1|bXoY$,:n=v7["_8unݤtsbQ[| A@\/>+G$b̋%?%1,"W_賐> /PxK@aNCbWEPA bNaYR7+2 Qcz>hbBOKfa+o"A@445BsVws~ 2˥ERm 4P1޼1>DP,,KdKJ#=-odUxX6;Ϩ:?$,ytb Av !fAȥI̋" =(X- %o^U8KK&Y #E qZI P8BY!9NeJU!h I5 s)8ޕؗ7]#AɠXf-'SVSrsNԃ8reM1 > :1<^M}(o|@,1$N€)DMzSۈ*6 Y6aŏ!0Pz *j!`>0Ù=p9҃X59r4WfMOipP&RX`UJՈJEOyTCDnjl܇13?3KPKgdWa2$@D]U߇]EbJwM,m&a7;P̉J8 f2\8eTuyn9E}5".!$z "ErEz㊉01<`Z3;3eLT6Isv} A ߔH 1ڵOd Y,@EsX|R֊ܑK`U|]@-/E,:ύ%+?(bY6TS6k '޵j30kTj1,%] 2Ye0tESdg %CT[ _d,Y !'rׄȷԄ65Uaplw^m#o};a:VT,^MSW*!D*jS@dRsQC~aa NW5Ѐ*3c(>-3Mh5zr6ӁXvv4<|󶙣eJ[SSڵREe 1 Ix<gPzfőT:G .kS\rm]o~ vRbmXSl38poLS[Uc&ymm) heEĵU%\ e"d7n4Ԡ;ÇkvDvyu5??ҹIвd3G%aN q"!QN!_hP6 ѥ(]ls<%إj:Rh R.B#ۺ`=Kَxm"݊ΰvȨ`~0g<\]%zETc!<\ȃZIcS֔<q2& ̝9}[K9i T@%3`79j*[Y;#ʆ҇yv$r 3ѧʊ&kmbS L|ɊC,d+45sfپd5a"cU%xz@q[M:eNp@1U`)#l$;-_g4]a9+٫bH16"1rǒ 6=vQTGW^YFg6;v:bѺ"`6-$ 1"@frR!-P pE_Ȥ ]:ohVpvymN6jQG1tch4 E}—|X3m]cNqղգ* &I:e0v ]Z=:QRA&݄S>.ND%$t^W}S~G.-KmU)h [ڮ&,WکF v:Ud3ƕ-/bE W44 \<,IYl60L zegˤOD= (PQx>\'}Z CA3@Xr &b8 $'S9k[ĆG@:'wZ4h:'L.6@ߢxD$g(ܼ0sKeZq^7h"Da|GgZzX'bԞ jq7Xe T(9=^)73>H-BÕxz @fLP,of;1AEl Q[ PC5s י‡1)C]+G vRNmBN_'L8y}xρr`W,5:(=1Xo*=h:Fwyo7dկ7]<\8ݫXxqr_`KI](z@r)bАGar˘]_j22tT:VܗƉHQymv,oa^1VΛE>\-ӼzՅט!*XLba?r]Wpw{42?(qط&XBx񰲨uwFT]8E̝NBt)^,Qu}`D{[`V˔M׵RjoPS^Nx~=o#`F*i6=ޕP|Z4+' |!"{[D2Lvh)]gti5 t/fuglqui1扜-ТȌ&xxxxxx3z /ihwjuj^V77Ƿa/k@ěw^4wzѥ2 Hh2^+M4#h`<(}tsff`]) Se&O#3-[]t6t#d*&\QY>rRggԣ%:rXHUR1#@bLLWn9VtU}o#@:iюӮC@vIm~S()XukFW0η';A/Hh}]2Ͽc֢zyG,X9E&RÙg ~*-̣8ʠh>qt;ǫNcǶWqØcDx ` T#q(|-ӓTbJ60ZeT7b-p{ ReȮ='V<6$ Ð zqh.y1@_@OWn٫qQrc۵7tX&ct' ZhtSg|;,RbݭV}G'OAxAuz:~[ pM$\`e&. +7siQuR(B¹j:cT,^yP>K`.Xv !n1S&xUwFl8NdN%[2Dޝj 2e4⾸sI3HDO`XL4򤌀{!@# 3["> |$. D0AEJpn^ƸޖXTB?Bo$Y밀dr[M&KzW=R <4{ntoe7,Aم 4$Z~ 0!ԕT֙TG_`‡vYU-#>u ]FRj_Wo o>mCVCh[F+ 4H//+ZU5zu}rHMI6>O)"g!2?%`Ym'C/@'Ɯ(mcYZ&=W4h+Wn[9%7 H?K=_D9.{Z$L**n aUQ+ gxh*kE] x8b!ioTDz *C mzqF!}P`T"b(9ר E8!:}ݷc[hZ"s#n&#+˶`gu(iU U|z++nC3 V,/FQ{J-%G^8lάb??=lSU1h0.ȝx 'y!X ڷG4l3\mK]li_2V4^2œF~85@N,>CO桇 _Pn e+TDZ*>R=6:(4kagtF0a(zyy~k*Vչ%{$LatvM 63+W4[(ҡ_Y13J䓈FFFN>6ƍ `$?nT؄H4%^.v&؆@d Ȭ#+3.3k[[I&5\X@|VE"9+lk\=jyA-jpmY5㴜b.F[I]Օ ;\IV; t?Tڱ[ҫC"7STʸİ+giFE((ՠ`0y\Gshwk05#%`H`2ѩ,;~Z)nxPgGO yR:WL.{/L>8Y$,-|o m<*q ŪQΠ&NJn`FsCl{Z`s"mREAN:{.~\qIT8JyPRpExbr`tΧ%-#\8ѐEڥ*L*,JuN 8e9ae2hxYop݁T^&vtxzo:c@w6g=< F X؝)2էbϢS6;sK+Rq x*l"TwHOPi<,xeIa7&d^Hg"Lv.Y'gW㒍()AZH#l3CBOuCKj전2MЗw+2<_ F0=SR\BROJGj~AϷN A!yR,ɰJt}!B0KͼauԔoڀH?SDx}b& Y =@oOf_*x3O\(H֭#4L|=f7x_C`*j|[؀`RBa&ǚUh ։bV:t n-ֵ Ređc9[!k?CuCoj;@7@@{v^onobKWxQ?{mRRa| 6mmG}S~iqiqiqiqiqxN +hu;1D̆FuNg*h]DR/TvʎSq*;Neǩ5SY.\hkq"K 9m$F~SDJ.f6mI. Q[q'B|ă,܄3"I8+!{KD"2bmUqx: BY?$1H ΪwS!ZηkVܯ7jju-1.1.0/test/portable-json5-tests.yaml000066400000000000000000000350451240215127700201600ustar00rootroot00000000000000# vi:set ts=2 sts=2 sw=2 et: # # Copyright (c) JD 2456730 Alex Kocharin # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # The original file is available here: # https://github.com/rlidwka/jju/tree/master/test/portable-json5-tests.yaml # # ---------------------------------------------------------------------------- # # Portable JSON5 test suite. # # This file contains an actual YAML data and it may include fancy syntax. # If your platform does not support YAML, you might wish to pre-process it # using a generic YAML parser. # %YAML 1.2 --- # # "input" is an arbitrary JSON5 you have to parse # "output" is a normalized JSON you have to compare your result with, # or !error (null) if your input should result in parser error # # Types of tests: # # - basic - Tests that every JSON5 parser should pass. # # - advanced - Tests that bring close compatibility with javascript. Not # strictly required, but nice to have for completeness. # # - extra - Extra test cases you can follow at your discretion. # # Questionable features like elisions go to extra. All valid javascript, but # invalid json5 also goes to extra. Feel free to ignore this section if you # want to. Thus, eval(input) is a complete json5 parser, that should pass all # basic and advanced tests. # # Basic types in minimal form # --------------------------- type-no-data: type: extra output: !error input: '' type-null: type: basic output: null input: > null # undefined is not supported, # null should be used instead type-no-undefined: type: extra output: !error input: > undefined type-no-raw: type: extra output: !error input: > foobar type-bool-true: type: basic output: true input: > true type-bool-false: type: basic output: false input: > false type-number: type: basic output: 0 input: > 0 type-string: type: basic output: "" input: > "" type-object: type: basic output: {} input: > {} type-array: type: basic output: [] input: > [] # Numbers: special # ---------------- # note: it's hard to test this # just add `1/x < 0` check in your code somewhere num-negative-zero: type: extra output: -0.0 input: > -0 num-nan: type: basic output: .nan input: > NaN num-signed-nan: type: basic output: .nan input: > +NaN num-positive-inf: type: basic output: +.inf input: > Infinity num-negative-inf: type: basic output: -.inf input: > -Infinity num-inf-exact-case: type: extra output: !error input: > INFINITY # Numbers: hexadecimal # -------------------- num-hex-zero: type: basic output: 0 input: > 0x0 num-cut-hex: type: basic output: !error input: > 0x num-all-hex: type: basic output: 12841684683518 input: > 0xBADF00DCAFE num-mixed-case: type: basic output: 3735928559 input: > 0xDeAdBEef num-signed-hex: type: advanced output: 31 input: > +0x1F num-negative-hex: type: advanced output: -31 input: > -0x1f num-bad-hex: type: advanced output: !error input: > 0xBADxF00D num-no-hex-float: type: advanced output: !error input: > 0x12.345 # this is not actually an exponent :) num-hex-exponent: type: advanced output: 4836 input: > 0x0012e4 # Numbers: octal # -------------- # Octals are primarily used in config files # to set up a file mask (like 0777) # # Note: they will have 0o12345 syntax instead # of 012345 in the ES6, so we'll need to switch # as well in the future num-octal: type: extra output: 342391 input: > 01234567 num-octal-zeroes: type: extra output: -24000 input: > -000000056700 num-bad-octal: type: extra output: !error input: > 012345678 num-no-octal-float: type: extra output: !error input: > 012.345 num-no-octal-exp: type: extra output: !error input: > 0123e4 # Numbers: floating point # ----------------------- num-float: type: basic output: 123.456 input: > 123.456 num-signed-foat: type: basic output: -0.00098765 input: > -0.00098765 num-omit-trailing-mantissa: type: basic output: 1234000 input: > 1234.e3 num-omit-leading-mantissa: type: advanced output: -123.4 input: > -.1234e3 num-bad-float: type: advanced output: !error input: > 0.12.345 num-bad-sum: type: extra output: !error input: > 0.12+345 num-uc-exp: type: advanced output: -1230000 input: > -123E+4 num-float-exp: type: basic output: 123000 input: > 0.0123e7 num-bad-exp: type: extra output: !error input: > 123e7.3 num-bad-char: type: extra output: !error input: > 123a456 num-no-exp: type: advanced output: !error input: > 123e num-zero-exp: type: advanced output: -0.0 input: > -.00e-0 num-dec-base-signed-exp: type: advanced output: 0.00000123 input: > 1230000E-012 # String: quotes # -------------- string-double-quotes: type: basic output: foobar input: > "foobar" string-single-quotes: type: basic output: foobar input: > 'foobar' string-open: type: basic output: !error input: > "\\\\\\\\\\\\\" string-not-open: type: basic output: "\\\\\\\\\\\\\\" input: > "\\\\\\\\\\\\\\" string-continuation: type: advanced output: " foo bar " input: > " foo \ bar \ " string-win-continuation: type: advanced output: "foobar" input: "'foo\\\r\nbar'" string-win-reverse-continuation: type: advanced output: !error input: "'foo\\\n\rbar'" string-unicode-continuation: type: advanced output: "foobarbaz" input: "'foo\\\u2028bar\\\u2029baz'" string-multi-bad-continuation: type: advanced output: !error input: > foo\ bar string-bad-ending: type: basic output: !error input: "'foo\rbar'" string-bad-ending-2028: type: advanced output: !error input: "'foo\u2028bar'" string-bad-ending-2029: type: advanced output: !error input: "'foo\u2029bar'" string-literal-unicode: type: advanced output: "foo\uFEFF\u2030bar\u1234" input: "'foo\uFEFF\u2030bar\u1234'" string-control-char: type: advanced output: "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f" input: "'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f'" # String: escape sequences # ------------------------ string-octal-escape: type: extra output: "\x1b[1;32mhi\x1b[m??" input: > '\033[1;32mhi\033[m\077\077' string-octal-two-digits: type: extra output: "\n\x1c\x2e\x07890\x01" input: > '\12\34\56\78\90\1' string-octal-three-digits: type: extra output: "\n34.78\xff 0" input: > '\01234\5678\377\400' string-hex-escape: type: extra output: "\x01\x23\xab\xcd\xef" input: > "\x01\x23\xab\xCd\xEF" # \0 is *not* an octal escape sequence, # and is allowed even in strict mode string-zero-point: type: basic output: "\0" input: > '\0' string-escape-double-quotes: type: basic output: "\"''" input: > "\"'\'" string-escape-single-quotes: type: basic output: " '\"\" " input: > ' \'"\" ' string-escape-json-chars: type: basic output: "\\\/\b\f\n\r\t" input: > "\\\/\b\f\n\r\t" # this character was left out of # json spec for whatever reason string-escape-slash-v: type: basic output: "\v" input: > "\v" string-unicode-escape: type: basic output: "\u0000\uffffx\ufeff\u1234\u9f6a\u2028\uabcd" input: > "\u0000\uFFFFx\uFeFf\u1234\u9F6a\u2028\uabcd" string-arbitrary-escape: type: advanced output: "X12Uqwe\r\tyiopasd\fghjklzc\u000b\b\nm9 " input: > '\X12\U\q\w\e\r\t\y\i\o\p\a\s\d\f\g\h\j\k\l\z\c\v\b\n\m\9\ ' string-bad-unicode: type: advanced output: !error input: > '\uEFGH' string-incomplete-unicode: type: advanced output: !error input: > '\u$' string-bad-hex: type: advanced output: !error input: > '\xFG' string-incomplete-hex: type: advanced output: !error input: > '\x$' # Object literals # --------------- object-nested: type: basic output: {q:{'w':{"e":[1]}}} input: | {q:{'w':{"e":[1]}}} object-trailing-comma: type: basic output: {foo: 'bar'} input: | {foo: 'bar',} object-leading-comma-style: type: basic output: {q: 1,w: 2,e: 3} input: | { q: 1 , w: 2 , e: 3 } object-incomplete: type: basic output: !error input: | {q:1,w:2,{} object-isnt-array: type: basic output: !error input: | {1,2} object-no-single-comma: type: basic output: !error input: | {,} object-no-elisions: type: basic output: !error input: | {q:1,,w:2} # Objects: keys # ------------- object-singlequoted: type: basic output: {q: 1,w: 2,e: 3} input: | {'q':1,'w':2,'e':3} object-doublequoted: type: basic output: {q: 1,w: 2,e: 3} input: | {"q":1,"w":2,"e":3} object-unquoted: type: basic output: {$FOO_bar123: 'baz'} input: > {$FOO_bar123: 'baz'} object-no-first-digit: type: advanced output: !error input: > {123foo: bar} object-unquoted-unicode: type: advanced output: {"\u1f04\u03bb\u03c6\u03b1": baz} input: "{\u1f04\u03bb\u03c6\u03b1:'baz'}" object-unicode-escape-key: type: advanced output: {foo: 'bar', "\u1f04\u03bb\u03c6\u03b1": baz, "qwe\u1f04rty": quux} input: | {foo:'bar', \u1f04\u03bb\u03c6\u03b1:'baz', qwe\u1f04rty: "quux"} object-no-raw-literal: type: extra output: !error input: | {foo: bar} object-bad-literal: type: advanced output: !error input: | {foo-bar: 123} object-no-space-in-key: type: advanced output: !error input: | {foo bar: 123} object-no-comment-in-key: type: advanced output: !error input: | {foo/*bar*/baz: 123} object-float-keys: type: advanced output: {'1': 'one', '3.1415': 'pi'} input: | {1:'one', 3.1415:'pi'} object-no-negative: type: advanced output: !error input: | {-5: 123} object-exponent-keys: type: advanced output: {'1': 'exp', '1000': 'pos', '0.001': 'neg'} input: | {1e0: 'exp', 1e+3: 'pos', 1e-3: 'neg'} object-octal-keys: type: extra output: {'668': 1} input: | {01234: 1} object-hex-keys: type: advanced output: {'51966': 1} input: | {0xCAFE: 1} object-null-keys: type: basic output: {'null': null} input: | {null: null} object-no-array-keys: type: extra output: !error input: | {[]: 123} object-no-empty-keys: type: basic output: !error input: | {: 123} object-empty-string-key: type: basic output: {s: {'': 1}, m: {'': 2}} input: | {s: {'': 1}, m: {"": 2}} object-bad-unicode-space: type: advanced output: !error input: | { \u0020foobar: 123 } object-bad-unicode-dash: type: advanced output: !error input: | { foo\u002dbar: 123} object-incomplete-unicode-sequence: type: advanced output: !error input: | { foo\u12f: 123 } object-double-escape: type: advanced output: !error input: | { foo\\u1234bar: 123 } object-we-arent-es3: type: basic output: {new: 1, delete: 2, throw: 3} input: | {new: 1, delete: 2, throw: 3} object-last-digits: type: basic output: {$123e2: 1, abc123: 2} input: | {$123e2: 1, abc123: 2} object-unicode-in-string: type: advanced output: {"\uff13qwe": 123} input: | {"\uff13qwe": 123} object-unicode-esc-in-string: type: advanced output: {"\\uff13qwe": 123} input: | {"\\uff13qwe": 123} object-unicode-digits-first-esc: type: advanced output: !error input: | {\uff13qwe: 123} object-unicode-digits-first-lit: type: advanced output: !error input: "{\uff13qwe: 123}" object-unicode-weirdness-esc: type: advanced output: {"digit\uff13": 1, "comb\u094F": 2, "punct\u2040": 3, "zwnj\u200C": 4} input: | {digit\uff13: 1, comb\u094F: 2, punct\u2040: 3, zwnj\u200C: 4} object-unicode-weirdness-lit: type: advanced output: {"digit\uff13": 1, "comb\u094F": 2, "punct\u2040": 3, "zwnj\u200C": 4} input: "{digit\uff13: 1, comb\u094F: 2, punct\u2040: 3, zwnj\u200C: 4}" # Array literals # -------------- array-all-types: type: basic output: [1.2,"3,4",{},[],null,+.inf] input: | [1.2,"3,4",{},[],null,Infinity] array-trailing-comma: type: basic output: [1,2,3,4] input: | [1,2,3,4,] array-leading-comma-style: type: basic output: [quux,foo,bar,baz] input: | [ 'quux' , 'foo' , 'bar' , 'baz' ] array-incomplete: type: basic output: !error input: | [1,2,3,[] array-nested: type: basic output: [[[[[[]]]]],[[],[]]] input: | [[[[[[/*[]*/]]]]],[[],[]]] array-isnt-object: type: extra output: !error input: | [1:2] array-no-single-comma: type: extra output: !error input: | [,] array-no-elisions: type: extra output: !error input: | [1,,2,3] # Comments # -------- comment-single: type: basic output: foobar input: | // blahblah "foobar" // another one comment-multi: type: basic output: foobar input: | /* * 123 */ "foobar" /**/ comment-single-newlines: type: advanced output: [ 123, 456, 789 ] input: "[// foo\r123,// bar\u2028456,// baz\u2029789]" comment-inside: type: advanced output: [123, '// foo', '/* bar'] input: > [ /* " // */ 123, // ", "// foo", '/* bar', ] comment-in-token: type: advanced output: !error input: 123/*comment*/456 comment-java-style: type: basic output: 123 input: /*****************/ 123 /****************/ comment-object: type: basic output: {q: 123} input: /**/{/**/q/**/:/**/123/**/,/**/}// # Whitespace # ---------- ws-no-whitespace: type: basic output: {"foo":bar,bar:["qwe",null,[],{}],"baz":123} input: '{foo:"bar","bar":["qwe",null,[],{}],"baz":123}' ws-allow-prefix: type: basic output: 123 input: " \t123" ws-unicode-spaces: type: advanced output: { foo : 123 } input: " \u0020\u00A0\uFEFF { \x09\x0A\x0B\x0C\x0D\u0085\u1680\u180E foo \u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A : \u2028\u2029\u202F\u205F\u3000 123 \uFEFF }" ws-unicode-newlines: type: advanced output: [ 123, 456 ] input: " [ \u000D 123, \u2028 456, \u2029 ] " # Multiple tokens # --------------- multi-string-nospace: type: basic output: !error input: '"foo""bar"' multi-string: type: basic output: !error input: '"foo" "bar"' # note: valid javascript multi-array: type: extra output: !error input: '[0] [0]' multi-object: type: basic output: !error input: '{} {}' ... jju-1.1.0/test/test_analyze.js000066400000000000000000000027441240215127700163300ustar00rootroot00000000000000var _assert = require('assert') var analyze = require('../').analyze function addTest(a, b) { if (typeof(describe) === 'function') { it('test_analyze: ' + a + ' == ' + b, function() { _assert.equal(a, b) }) } else { _assert.equal(a, b) } } var t = analyze(JSON.stringify(require('os').networkInterfaces())) addTest(t.has_whitespace, false) addTest(t.has_comments, false) addTest(t.has_newlines, false) addTest(t.newline, '\n') addTest(t.quote, '"') addTest(t.quote_keys, true) addTest(t.indent, '') var t = analyze(JSON.stringify(require('os').networkInterfaces(), null, 2)) addTest(t.has_whitespace, true) addTest(t.has_comments, false) addTest(t.has_newlines, true) addTest(t.newline, '\n') addTest(t.quote, '"') addTest(t.quote_keys, true) addTest(t.indent, ' ') var t = analyze(JSON.stringify(require('os').networkInterfaces(), null, 3)) addTest(t.indent, ' ') var t = analyze(JSON.stringify(require('os').networkInterfaces(), null, '\t')) addTest(t.indent, '\t') var t = analyze(JSON.stringify(require('os').networkInterfaces(), null, 3).replace(/\n/g, '\r\n')) addTest(t.indent, ' ') addTest(t.newline, '\r\n') var t = analyze(JSON.stringify(require('os').networkInterfaces()).replace(/"/g, "'")) addTest(t.quote, "'") addTest(t.quote_keys, true) var t = analyze("{foo:'bar', 'bar':\"baz\", 'baz':\"quux\"}") addTest(t.quote, "'") addTest(t.quote_keys, false) var t = analyze("{foo:'bar', \"bar\":'baz', \"baz\":\"quux\"}") addTest(t.quote, '"') addTest(t.quote_keys, false) jju-1.1.0/test/test_document.js000066400000000000000000000225641240215127700165050ustar00rootroot00000000000000 var assert = require('assert') var create = require('../lib/document').Document var jju = require('..') var str = '{ x\r\n:\n1, y: {"..z.": 123, t: null, s:"123", a:[ 1,2,{x:3},] }}\n' var d = create(str) assert.equal(d + '', str) assert.deepEqual(d.get(''), {x:1,y:{'..z.':123,t:null,s:'123',a:[1,2,{x:3}]}}) assert.deepEqual(d.get('x'), 1) assert.deepEqual(d.get('x.x'), undefined) assert.deepEqual(d.get('x.x.x.x'), undefined) assert.strictEqual(d.get('y.x'), undefined) assert.deepEqual(d.get('y.s'), '123') assert.strictEqual(d.get('y.t'), null) assert.strictEqual(d.get('y.t.x'), undefined) assert.equal(d.has(''), true) assert.equal(d.has('x'), true) assert.equal(d.has('x.x'), false) assert.equal(d.has('x.x.x.x'), false) assert.equal(d.has('y.x'), false) assert.equal(d.has('y'), true) assert.equal(d.has('y.s'), true) assert.equal(d.has('y.t'), true) assert.equal(d.has('a'), false) // arrays assert.deepEqual(d.get('y.a'), [1,2,{x:3}]) assert.deepEqual(d.get('y.a.0'), 1) assert.deepEqual(d.get('y.a.2.x'), 3) assert.deepEqual(d.get('y.a.10'), undefined) assert.deepEqual(d.has('y.a.0'), true) assert.deepEqual(d.has('y.a.10'), false) assert.deepEqual(d.get('y.a.2'), {x:3}) assert.deepEqual(d.get('y.a.-1'), undefined) // controversial assert.strictEqual(d.get('y.s.0'), undefined) assert.equal(d.has('y.s.0'), false) // paths assert.deepEqual(d.get([]), {x:1,y:{'..z.':123,t:null,s:'123',a:[1,2,{x:3}]}}) assert.strictEqual(d.has([]), true) assert.strictEqual(d.get(['y','..z.']), 123) assert.strictEqual(d.has(['y','..z.']), true) assert.deepEqual(d.get(['y','a',2,'x']), 3) assert.deepEqual(create('[1]').set(0, 4).get(''), [4]) assert.deepEqual(create('[1]').set(1, 4).get(''), [1,4]) assert.deepEqual(create('[1]').has(0), true) assert.deepEqual(create('[1]').has(1), false) assert.deepEqual(create('[1]').get(0), 1) // invalid paths assert.throws(function() { create('[1]').set(null, 4) }, /invalid path type/i) assert.throws(function() { create('[1]').set({}, 4) }, /invalid path type/i) assert.throws(function() { create('[1]').set(/./, 4) }, /invalid path type/i) assert.throws(function() { create('[1]').set(function(){}, 4) }, /invalid path type/i) assert.throws(function() { create('[1]').set(false, 4) }, /invalid path type/i) assert.throws(function() { create('[1]').set(undefined, 4) }, /invalid path type/i) // set root assert.strictEqual(create(str).set('', 4).get(''), 4) assert.strictEqual(create(str).set('', null).get(''), null) assert.strictEqual(create(str).set('', {x:4}).get('x'), 4) assert.deepEqual(create(str).set('', [1,2,3]).get(''), [1,2,3]) assert.strictEqual(create('1').set('', 4).get(''), 4) assert.strictEqual(create('null').set('', 4).get(''), 4) assert.strictEqual(create('[]').set('', 4).get(''), 4) assert.strictEqual(create('{}').set('', 4).get(''), 4) // set 1st level assert.deepEqual(create('{}').set('x', 4).get('x'), 4) assert.deepEqual(create('{a:{b:[]}}').set('a.b.0', 4).get('a'), {b:[4]}) //assert.deepEqual(create('1').set('x', 4).get('x'), 4) //assert.deepEqual(create('null').set('x', 4).get('x'), 4) // array: boundaries assert.strictEqual(create('[]').set('0', 4).get('0'), 4) assert.strictEqual(create('[1,2,3]').set('2', 4).get('2'), 4) assert.strictEqual(create('[1,2,3]').set('3', 4).get('3'), 4) // various error cases assert.throws(function() { create('1').set('x', 4) }, /set key .* of an non-object/) assert.throws(function() { create('null').set('x', 4) }, /set key .* of an non-object/) assert.throws(function() { create('[]').set('x', 4) }, /set key .* of an array/) assert.throws(function() { create('""').set('x', 4) }, /set key .* of an non-object/) assert.throws(function() { create('{}').set('x.x.x', 4) }, /set key .* of an non-object/) assert.throws(function() { create('1').set('1', 4) }, /set key .* of an non-object/) assert.throws(function() { create('null').set('1', 4) }, /set key .* of an non-object/) assert.throws(function() { create('""').set('1', 4) }, /set key .* of an non-object/) assert.throws(function() { create('[]').set('-1', 4) }, /set key .* of an array/) assert.throws(function() { create('[]').set('1', 4) }, /set key .* out of bounds/) assert.throws(function() { create('[1,2,3]').set('4', 4) }, /set key .* out of bounds/) assert.throws(function() { create('{a:{b:[]}}').set('a.b.x', 4) }, /set key .* of an array/) // unsetting stuff assert.throws(function() { create('[]').unset('') }, /can't remove root document/) // arrays: handling spaces correctly assert.equal(create("[]").set(0,{})+"", '[{}]') assert.equal(create("[0]").set(1,{})+"", '[0,{}]') assert.equal(create("[0,]").set(1,{})+"", '[0,{},]') assert.equal(create("[ ]").set(0,{})+"", '[{} ]') assert.equal(create("[ 0 , ]").set(1,{})+"", '[ 0 , {}, ]') assert.equal(create("[ 0 ]").set(1,{})+"", '[ 0, {} ]') assert.equal(create("{}").set('y',{})+"", '{"y":{}}') assert.equal(create("{x:1}").set('y',{})+"", '{x:1,y:{}}') assert.equal(create("{x:1,}").set('y',{})+"", '{x:1,y:{},}') assert.equal(create("{ }").set('y',{})+"", '{"y":{} }') assert.equal(create("{ x:1 , }").set('y',{})+"", '{ x:1 , y:{}, }') assert.equal(create("{ x:1 }").set('y',{})+"", '{ x:1, y:{} }') // deleting elements assert.throws(function() { create('[]').unset('0') }, /unset key .* out of bounds/) assert.throws(function() { create('[1,2]').unset('2') }, /unset key .* out of bounds/) assert.throws(function() { create('[1,2,3]').unset('0') }, /in the middle of an array/) // CommonJS assert spec is "awesome" assert.deepEqual(create('[1,2]').unset('1').get(''), [1]) assert.deepEqual(create('[1,2]').unset('1').get('').length, 1) assert.deepEqual(create('[1,2,3]').unset('2').unset('1').get(''), [1]) assert.deepEqual(create('[1,2,3]').unset('2').unset('1').get('').length, 1) assert.deepEqual(create('[1]').unset('0').get(''), []) assert.deepEqual(create('[1]').unset('0').get('').length, 0) assert.deepEqual(create('{x:{y:"z"}, z:4}').unset('x').get(''), {z:4}) assert.throws(function() { create('[1,2]').unset('') }, /root/) // getting crazy //assert.deepEqual(create(str).set('a.b.c.d.e', 1).get('a'), {b:{c:{d:{e:1}}}}) // update: arrays assert.deepEqual(create("[1]").update([2,3])+"", '[2,3]') assert.deepEqual(create("[1]").update([2,3,4])+"", '[2,3,4]') assert.deepEqual(create("[]").update([2])+"", '[2]') assert.deepEqual(create("[2]").update([])+"", '[]') assert.deepEqual(create("[2,3,4]").update([2,3])+"", '[2,3]') assert.deepEqual(create("[2,3,4]").update([])+"", '[]') assert.deepEqual(create("[]").update([2,3,4])+"", '[2,3,4]') assert.deepEqual(create(" /*zz*/ [ 2 , 3 , 4 ] /*xx*/ ").update([])+"", ' /*zz*/ [ ] /*xx*/ ') assert.deepEqual(create(" /*zz*/ [ ] /*xx*/ ").update([2,3,4])+"", ' /*zz*/ [2,3,4 ] /*xx*/ ') // update: objects assert.deepEqual(create("{x:1}").update({x:1,y:2,z:3})+"", '{x:1,y:2,z:3}') assert.deepEqual(create("{x:1}").update({x:2,z:3,t:4})+"", '{x:2,z:3,t:4}') assert.deepEqual(create("{}").update({x:1,y:2})+"", '{"x":1,"y":2}') assert.deepEqual(create("{x:1}").update({})+"", '{}') assert.deepEqual(create("{x:1,y:2}").update({x:1})+"", '{x:1}') assert.deepEqual(create(" /*zz*/ { x /*a*/ : /*b*/ 2 , y:3 , z //\n: 4 } /*xx*/ ").update({})+"", ' /*zz*/ { } /*xx*/ ') assert.deepEqual(create(" /*zz*/ { } /*xx*/ ").update({x: 2, y: 3, z: 4})+"", ' /*zz*/ {"x":2,"y":3,"z":4 } /*xx*/ ') // remove trailing comma assert.deepEqual(create("{x:1,}").update({})+"", '{}') assert.deepEqual(create("[0,]").update([])+"", '[]') assert.deepEqual(create("[0 /*z*/ , /*z*/]").update([])+"", '[ /*z*/]') // mode assert.equal(create('{"test":123}', {mode:'json'}).update({q:1,w:2})+'', '{"q":1,"w":2}') assert.equal(create('{1:2}').update({ a: 1, b: [1,2], c: 3})+'', '{a:1,b:[1,2],c:3}') // undef //assert.throws(function(){ jju.update(undefined, undefined) }, /root doc/) assert.equal(jju.update(undefined, undefined), '') assert.equal(jju.update(undefined, 42), '42') assert.equal(jju.update(undefined, {x: 5}), '{"x":5}') /* * real test */ var upd = { name: 'yapm', version: '0.6.0', description: 'npm wrapper allowing to use package.yaml instead of package.json', author: { name: 'Alex Kocharin', email: 'alex@kocharin.ru' }, keywords: [ 'package manager', 'modules', 'install', 'package.yaml', 'package.json5', 'yaml', 'json5', 'npm' ], preferGlobal: true, homepage: 'https://npmjs.org/doc/', repository: { type: 'git', url: 'https://github.com/rlidwka/yapm' }, bugs: { url: 'http://github.com/rlidwka/yapm/issues' }, main: './yapm.js', bin: { yapm: './yapm.js' }, dependencies: { npm: '*', 'js-yaml': '*', through: '*', 'json5-utils': '*' }, devDependencies: { async: '*' }, optionalDependencies: { 'yaml-update': '*' }, test_nonascii: 'тест' } assert.deepEqual(create(create('{"garbage":"garbage"}').update(upd)).get(''), upd) assert.deepEqual(JSON.parse(create('{"garbage":"garbage"}', {mode:'json',legacy:true}).update(upd)), upd) //console.log(create('{"garbage":"garbage"}').update(upd)+'') //assert.deepEqual(create(" [ ] //").set(0,{})+"" [ ,{}] // //node -e 'console.log(require("./document").Document("{}").set("",[1,2,3])+"")'[1, 2, 3] //alex@elu:~/json5-utils/lib$ node -e 'console.log(require("./document").Document("[]").set("0",[1,2,3]).get(""))' //[ [ 1, 2, 3 ] ] /*assert.equal(create('"test"').get(''), 'test') assert.equal(create('"test"').get([]), 'test') assert.equal(create('"test"').get(false), 'test') assert.equal(create(undefined).get(''), undefined) //assert.equal(create('"test"').set('', 'foo').toString(), '"foo"') */ jju-1.1.0/test/test_errors.js000066400000000000000000000025311240215127700161730ustar00rootroot00000000000000var assert = require('assert') var parse = require('../').parse function addTest(arg, row, col, errRegExp) { var fn = function() { try { parse(arg) } catch(err) { if (row !== undefined) assert.equal(err.row, row, 'wrong row: ' + err.row) if (col !== undefined) assert.equal(err.column, col, 'wrong column: ' + err.column) if (errRegExp) assert(errRegExp.exec(err.message)) return } throw new Error("no error") } if (typeof(describe) === 'function') { it('test_errors: ' + JSON.stringify(arg), fn) } else { fn() } } // semicolon will be unexpected, so it indicates an error position addTest(';', 1, 1) addTest('\n\n\n;', 4, 1) addTest('\r\n;', 2, 1) addTest('\n\r;', 3, 1) addTest('\n\u2028;', 3, 1) addTest('\n\u2029;', 3, 1) addTest('[\n1\n,\n;', 4, 1) addTest('{\n;', 2, 1) addTest('{\n1\n:\n;', 4, 1) addTest('.e3', 1, 3, /"\.e3"/) // line continuations addTest('["\\\n",\n;', 3, 1) addTest('["\\\r\n",\n;', 3, 1) addTest('["\\\u2028",\n;', 3, 1) addTest('["\\\u2029",\n;', 3, 1) // bareword rewind addTest('nulz', 1, 1) // no data addTest(' ', 1, 3, /No data.*whitespace/) addTest('blah', 1, 1, /Unexpected token 'b'/) addTest('', 1, 1, /No data.*empty input/) try { parse('{{{{{{{{{') } catch(err) { var x = err.stack.match(/parseObject/g) assert(!x || x.length < 2, "shouldn't blow up the stack with internal calls") } jju-1.1.0/test/test_parse.js000066400000000000000000000103151240215127700157700ustar00rootroot00000000000000 var assert = require('assert') var parse = require('../').parse function addTest(arg, bulk) { function fn_json5() { //console.log('testing: ', arg) try { var x = parse(arg) } catch(err) { x = 'fail' } try { var z = eval('(function(){"use strict"\nreturn ('+String(arg)+'\n)\n})()') } catch(err) { z = 'fail' } assert.deepEqual(x, z) } function fn_strict() { //console.log('testing: ', arg) try { var x = parse(arg, {mode: 'json'}) } catch(err) { x = 'fail' } try { var z = JSON.parse(arg) } catch(err) { z = 'fail' } assert.deepEqual(x, z) } if (typeof(describe) === 'function' && !bulk) { it('test_parse_json5: ' + JSON.stringify(arg), fn_json5) it('test_parse_strict: ' + JSON.stringify(arg), fn_strict) } else { fn_json5() fn_strict() } } addTest('"\\uaaaa\\u0000\\uFFFF\\uFaAb"') addTest(' "\\xaa\\x00\xFF\xFa\0\0" ') addTest('"\\\'\\"\\b\\f\\t\\n\\r\\v"') addTest('"\\q\\w\\e\\r\\t\\y\\\\i\\o\\p\\[\\/\\\\"') addTest('"\\\n\\\r\n\\\n"') addTest('\'\\\n\\\r\n\\\n\'') addTest(' null') addTest('true ') addTest('false') addTest(' Infinity ') addTest('+Infinity') addTest('[]') addTest('[ 0xA2, 0X024324AaBf]') addTest('-0x12') addTest(' [1,2,3,4,5]') addTest('[1,2,3,4,5,] ') addTest('[1e-13]') addTest('[null, true, false]') addTest(' [1,2,"3,4,",5,]') addTest('[ 1,\n2,"3,4," \r\n,\n5,]') addTest('[ 1 , 2 , 3 , 4 , 5 , ]') addTest('{} ') addTest('{"2":1,"3":null,}') addTest('{ "2 " : 1 , "3":null , }') addTest('{ \"2\" : 25e245 , \"3\": 23 }') addTest('{"2":1,"3":nul,}') addTest('{:1,"3":nul,}') addTest('[1,2] // ssssssssss 3,4,5,] ') addTest('[1,2 , // ssssssssss \n//xxx\n3,4,5,] ') addTest('[1,2 /* ssssssssss 3,4,*/ /* */ , 5 ] ') addTest('[1,2 /* ssssssssss 3,4,*/ /* * , 5 ] ') addTest('{"3":1,"3":,}') addTest('{ чйуач:1, щцкшчлм : 4,}') addTest('{ qef-:1 }') addTest('{ $$$:1 , ___: 3}') addTest('{3:1,2:1}') addTest('{3.4e3:1}') addTest('{-3e3:1}') addTest('{+3e3:1}') addTest('{.3e3:1}') for (var i=0; i<200; i++) { addTest('"' + String.fromCharCode(i) + '"', true) } // strict JSON test cases addTest('"\\xaa"') addTest('"\\0"') addTest('"\0"') addTest('"\\v"') addTest('{null: 123}') addTest("{'null': 123}") assert.throws(function() { parse('0o') }) assert.strictEqual(parse('01234567'), 342391) assert.strictEqual(parse('0o1234567'), 342391) // undef assert.strictEqual(parse(undefined), undefined) // whitespaces addTest('[1,\r\n2,\r3,\n]') '\u0020\u00A0\uFEFF\x09\x0A\x0B\x0C\x0D\u0085\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000'.split('').forEach(function(x) { addTest(x+'[1,'+x+'2]'+x) addTest('"'+x+'"'+x) }) '\u000A\u000D\u2028\u2029'.split('').forEach(function(x) { addTest(x+'[1,'+x+'2]'+x) addTest('"\\'+x+'"'+x) }) /* weird ES6 stuff, not working if (process.version > 'v0.11.7') { assert(Array.isArray(parse('{__proto__:[]}').__proto__)) assert.equal(parse('{__proto__:{xxx:5}}').xxx, undefined) assert.equal(parse('{__proto__:{xxx:5}}').__proto__.xxx, 5) var o1 = parse('{"__proto__":[]}') assert.deepEqual([], o1.__proto__) assert.deepEqual(["__proto__"], Object.keys(o1)) assert.deepEqual([], Object.getOwnPropertyDescriptor(o1, "__proto__").value) assert.deepEqual(["__proto__"], Object.getOwnPropertyNames(o1)) assert(o1.hasOwnProperty("__proto__")) assert(Object.prototype.isPrototypeOf(o1)) // Parse a non-object value as __proto__. var o2 = JSON.parse('{"__proto__":5}') assert.deepEqual(5, o2.__proto__) assert.deepEqual(["__proto__"], Object.keys(o2)) assert.deepEqual(5, Object.getOwnPropertyDescriptor(o2, "__proto__").value) assert.deepEqual(["__proto__"], Object.getOwnPropertyNames(o2)) assert(o2.hasOwnProperty("__proto__")) assert(Object.prototype.isPrototypeOf(o2)) }*/ assert.throws(parse.bind(null, "{-1:42}")) for (var i=0; i<100; i++) { var str = '-01.e'.split('') var rnd = [1,2,3,4,5].map(function(x) { x = ~~(Math.random()*str.length) return str[x] }).join('') try { var x = parse(rnd) } catch(err) { x = 'fail' } try { var y = JSON.parse(rnd) } catch(err) { y = 'fail' } try { var z = eval(rnd) } catch(err) { z = 'fail' } //console.log(rnd, x, y, z) if (x !== y && x !== z) throw 'ERROR' } jju-1.1.0/test/test_portable.js000066400000000000000000000021571240215127700164730ustar00rootroot00000000000000var assert = require('assert') , fs = require('fs') , YAML = require('js-yaml') , jju = require('../') function addTest(name, fn) { if (typeof(describe) === 'function') { it(name, fn) } else { fn() } } var schema = YAML.Schema.create([ new YAML.Type('!error', { kind: 'scalar', resolve: function (state) { //state.result = null return true }, }) ]) var tests = YAML.safeLoad(fs.readFileSync(__dirname + '/portable-json5-tests.yaml', 'utf8'), { schema: schema }) if (!Object.is) { Object.defineProperty(Object, 'is', { value: function(x, y) { if (x === y) { return x !== 0 || 1 / x === 1 / y; } return x !== x && y !== y; }, configurable: true, enumerable: false, writable: true, }) } for (var k in tests) { ;(function(k) { addTest(k, function() { try { var result = jju.parse(tests[k].input) } catch(err) { result = null } // need deepStrictEqual if (typeof(result) === 'object') { assert.deepEqual(result, tests[k].output) } else { assert(Object.is(result, tests[k].output), String(result) + ' == ' + tests[k].output) } }) })(k) } jju-1.1.0/test/test_stringify.js000066400000000000000000000051611240215127700166770ustar00rootroot00000000000000 var assert = require('assert') var parse = require('../').parse var stringify = require('../').stringify function deepEqual(x, y) { if (Number.isNaN(x)) { return assert(Number.isNaN(y)) } assert.deepEqual(x, y) } function addTest(arg, arg2, arg3) { function fn() { deepEqual(parse(stringify(arg)), arg2 === undefined ? arg : arg2) if (arg !== undefined) deepEqual(JSON.parse(stringify(arg, {mode: 'json', indent: false})), (arg3 === undefined ? (arg2 === undefined ? arg : arg2) : arg3)) } if (typeof(describe) === 'function') { it('test_stringify: ' + JSON.stringify(arg), fn) } else { fn() } } addTest(0) addTest(-0) addTest(NaN, undefined, null) addTest(Infinity, undefined, null) addTest(-Infinity, undefined, null) addTest(123) addTest(19508130958019385.135135) addTest(-2e123) addTest(null) addTest(undefined) addTest([]) addTest([,,,,,,,], [null,null,null,null,null,null,null]) addTest([undefined,null,1,2,3,], [null,null,1,2,3]) addTest([[[[]]],[[]]]) addTest({}) addTest({1:2,3:4}) addTest({1:{1:{1:{1:4}}}, 3:4}) addTest({1:undefined, 3:undefined}, {}) addTest(new Number(4), 4) addTest(new Boolean(true), true) addTest(new String('xqefxef'), 'xqefxef') addTest(new Boolean(), false) var r='';for (var i=0; i<5000; i++) {r+=String.fromCharCode(i)} addTest(r) assert.equal("[1, 2, 3]", stringify([1, 2, 3], {indent: 1})) assert.equal("[1, 2, 3]", stringify([1, 2, 3], {indent: 2})) var oddball = Object(42) oddball.__proto__ = { __proto__: null } assert.equal('{}', stringify(oddball)) /* this WILL throw var falseNum = Object("37") falseNum.__proto__ = Number.prototype assert.equal("{0: '3', 1: '7'}", stringify(falseNum))*/ assert.equal(stringify(Infinity), 'Infinity') assert.equal(stringify(Infinity, {mode: 'json'}), 'null') assert.equal(stringify(NaN), 'NaN') assert.equal(stringify(NaN, {mode: 'json'}), 'null') assert.equal(stringify(-0), '-0') assert.equal(stringify('test', null), "'test'") var array = [""] var expected = "''" for (var i = 0; i < 1000; i++) { array.push("") expected = "''," + expected } expected = '[' + expected + ']' assert.equal(expected, stringify(array, {indent: false})) assert.strictEqual(stringify([1,2,3], function(){}), undefined) // don't stringify prototype assert.equal('{a: 1}', stringify({a:1,__proto__:{b:2}})) // sort keys tests assert.equal('{a: 1, b: 2, z: 3}', stringify({b:2,a:1,z:3}, {sort_keys: 1})) assert.equal('{a: 1, b: {a: 2, b: 5, c: 1}, z: 3}', stringify({b:{c:1,a:2,b:5},a:1,z:3}, {sort_keys: 1})) assert.equal('{a: [3, 5, 1], b: 2, z: 3}', stringify({b:2,a:[3,5,1],z:3}, {sort_keys: 1})) assert.equal('{b: 2, a: 1, z: 3}', stringify({b:2,a:1,z:3}, {sort_keys: 0})) jju-1.1.0/test/test_tokenize.js000066400000000000000000000076471240215127700165240ustar00rootroot00000000000000var assert = require('assert') var parse = require('../').parse function tokenize(arg) { var result = [] parse(arg, {_tokenize: function(smth) { result.push(smth) }}) assert.deepEqual(result.map(function(x){return x.raw}).join(''), arg) return result } function addTest(x, exp) { function fn(){assert.deepEqual(tokenize(x), exp)} if (typeof(describe) === 'function') { it('test_tokenize: ' + JSON.stringify(x), fn) } else { fn() } } addTest('123', [ { raw: '123', value: 123, type: 'literal', stack: [] }]) addTest(' /* zz */\r\n true /* zz */\n', [ { raw: ' ', type: 'whitespace', stack: [] }, { raw: '/* zz */', type: 'comment', stack: [] }, { raw: '\r\n', type: 'newline', stack: [] }, { raw: ' ', type: 'whitespace', stack: [] }, { raw: 'true', type: 'literal', value: true, stack: [] }, { raw: ' ', type: 'whitespace', stack: [] }, { raw: '/* zz */', type: 'comment', stack: [] }, { raw: '\n', type: 'newline', stack: [] } ]) addTest('{q:123, w : /*zz*/\n\r 345 } ', [ { raw: '{', type: 'separator', stack: [] }, { raw: 'q', type: 'key', value: 'q', stack: [] }, { raw: ':', type: 'separator', stack: [] }, { raw: '123', type: 'literal', value: 123, stack: ['q'] }, { raw: ',', type: 'separator', stack: [] }, { raw: ' ', type: 'whitespace', stack: [] }, { raw: 'w', type: 'key', value: 'w', stack: [] }, { raw: ' ', type: 'whitespace', stack: [] }, { raw: ':', type: 'separator', stack: [] }, { raw: ' ', type: 'whitespace', stack: [] }, { raw: '/*zz*/', type: 'comment', stack: [] }, { raw: '\n', type: 'newline', stack: [] }, { raw: '\r', type: 'newline', stack: [] }, { raw: ' ', type: 'whitespace', stack: [] }, { raw: '345', type: 'literal', value: 345, stack: ['w'] }, { raw: ' ', type: 'whitespace', stack: [] }, { raw: '}', type: 'separator', stack: [] }, { raw: ' ', type: 'whitespace', stack: [] } ]) addTest('null /* */// xxx\n//xxx', [ { raw: 'null', type: 'literal', value: null, stack: [] }, { raw: ' ', type: 'whitespace', stack: [] }, { raw: '/* */', type: 'comment', stack: [] }, { raw: '// xxx', type: 'comment', stack: [] }, { raw: '\n', type: 'newline', stack: [] }, { raw: '//xxx', type: 'comment', stack: [] } ]) addTest('[1,2,[[],[1]],{},{1:2},{q:{q:{}}},]', [ { raw: '[', type: 'separator', stack: [] }, { raw: '1', type: 'literal', value: 1, stack: [0] }, { raw: ',', type: 'separator', stack: [] }, { raw: '2', type: 'literal', value: 2, stack: [1] }, { raw: ',', type: 'separator', stack: [] }, { raw: '[', type: 'separator', stack: [2] }, { raw: '[', type: 'separator', stack: [2,0] }, { raw: ']', type: 'separator', stack: [2,0] }, { raw: ',', type: 'separator', stack: [2] }, { raw: '[', type: 'separator', stack: [2,1] }, { raw: '1', type: 'literal', value: 1, stack: [2,1,0] }, { raw: ']', type: 'separator', stack: [2,1] }, { raw: ']', type: 'separator', stack: [2] }, { raw: ',', type: 'separator', stack: [] }, { raw: '{', type: 'separator', stack: [3] }, { raw: '}', type: 'separator', stack: [3] }, { raw: ',', type: 'separator', stack: [] }, { raw: '{', type: 'separator', stack: [4] }, { raw: '1', type: 'key', value: 1, stack: [4] }, { raw: ':', type: 'separator', stack: [4] }, { raw: '2', type: 'literal', value: 2, stack: [4,'1'] }, { raw: '}', type: 'separator', stack: [4] }, { raw: ',', type: 'separator', stack: [] }, { raw: '{', type: 'separator', stack: [5] }, { raw: 'q', type: 'key', value: 'q', stack: [5] }, { raw: ':', type: 'separator', stack: [5] }, { raw: '{', type: 'separator', stack: [5,'q'] }, { raw: 'q', type: 'key', value: 'q', stack: [5,'q'] }, { raw: ':', type: 'separator', stack: [5,'q'] }, { raw: '{', type: 'separator', stack: [5,'q','q'] }, { raw: '}', type: 'separator', stack: [5,'q','q'] }, { raw: '}', type: 'separator', stack: [5,'q'] }, { raw: '}', type: 'separator', stack: [5] }, { raw: ',', type: 'separator', stack: [] }, { raw: ']', type: 'separator', stack: [] } ]) jju-1.1.0/test/test_updates.js000066400000000000000000000010271240215127700163230ustar00rootroot00000000000000var assert = require('assert') , fs = require('fs') , YAML = require('js-yaml') , jju = require('../') function addTest(name, fn) { if (typeof(describe) === 'function') { it(name, fn) } else { fn() } } fs.readdirSync(__dirname + '/update').filter(function(file) { return file.match(/^[^\.].*\.yaml$/) }).forEach(function(file) { addTest('update: ' + file, function() { var test = YAML.load(fs.readFileSync(__dirname + '/update/' + file, 'utf8')) assert.strictEqual(test.test(jju, test.input), test.output) }) }) jju-1.1.0/test/update/000077500000000000000000000000001240215127700145435ustar00rootroot00000000000000jju-1.1.0/test/update/author.yaml000066400000000000000000000014141240215127700167310ustar00rootroot00000000000000input: | { "name": "just-a-demo", "version": "0.1.2", "description": "blahblahblah", "main": "test.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "John Doe ", "license": "BSD-2-Clause" } output: | { "name": "just-a-demo", "version": "0.1.2", "description": "blahblahblah", "main": "test.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": { "name": "John Doe", "email": "whoever@google.com" }, "license": "BSD-2-Clause" } test: !!js/function | function(jju, input) { obj = jju.parse(input) obj.author = { name: 'John Doe', email: 'whoever@google.com', } return jju.update(input, obj) } jju-1.1.0/test/update/deep-object.yaml000066400000000000000000000007751240215127700176210ustar00rootroot00000000000000input: | { "foo": { "bar": { "baz": { "quux": "4" } } } } output: | { "foo": { "bar": { "baz": { "quux": "4" }, "qwe": { "rty": { "aaa": { "bbb": 1 } } } } } } test: !!js/function | function(jju, input) { obj = jju.parse(input) obj.foo.bar.qwe = {rty: {aaa: {bbb: 1}}} return jju.update(input, obj, {mode:'json'}) } jju-1.1.0/test/update/delete.yaml000066400000000000000000000011361240215127700166720ustar00rootroot00000000000000input: | { "name": "test", "version": "0.0.0", "dependencies": { "foo": "1.2.x", "bar": ">= 1" }, "bundleDependencies": [ "foo", "bar" ], "license": "BSD-2-Clause" } output: | { "name": "test", "version": "0.0.0", "dependencies": { "foo": "1.2.x" }, "bundleDependencies": [ "foo" ], "license": "BSD-2-Clause" } test: !!js/function | function(jju, input) { obj = jju.parse(input) obj.bundleDependencies.pop() delete obj.dependencies.bar return jju.update(input, obj, {mode:'json'}) } jju-1.1.0/test/update/norm-array.yaml000066400000000000000000000010331240215127700175130ustar00rootroot00000000000000input: | { "name": "test", "version": "0.0.0", "bundleDependencies": [ "foo", "bar" ], "license": "BSD-2-Clause" } output: | { "name": "test", "version": "0.0.0", "bundleDependencies": [ "foo", "bar", "baz", "quux" ], "license": "BSD-2-Clause" } test: !!js/function | function(jju, input) { obj = jju.parse(input) obj.bundleDependencies.push('baz') obj.bundleDependencies.push('quux') return jju.update(input, obj, {mode:'json'}) } jju-1.1.0/test/update/norm-object.yaml000066400000000000000000000011171240215127700176460ustar00rootroot00000000000000input: | { "name": "test", "version": "0.0.0", "dependencies": { "foobar": "*", "bazquux": ">= 1.1.1" }, "license": "BSD-2-Clause" } output: | { "name": "test", "version": "0.0.0", "dependencies": { "foobar": "*", "bazquux": ">= 1.1.1", "whatever": "1.2.x", "qwerty": "1" }, "license": "BSD-2-Clause" } test: !!js/function | function(jju, input) { obj = jju.parse(input) obj.dependencies.whatever = '1.2.x' obj.dependencies.qwerty = '1' return jju.update(input, obj, {mode:'json'}) } jju-1.1.0/test/update/npm-array-bin.yaml000066400000000000000000000020121240215127700200760ustar00rootroot00000000000000input: | { "name":"npm-test-array-bin" , "version":"1.2.5" , "bin": [ "bin/array-bin" ] , "scripts": { "test": "node test.js" } } # less than ideal, I know... output: | { "name":"npm-test-array-bin" , "version":"1.2.5" , "bin": {"array-bin":"bin/array-bin"} , "scripts": { "test": "node test.js" }, "readme": "just an npm test\n", "readmeFilename": "README", "description": "just an npm test", "_id": "npm-test-array-bin@1.2.5", "dist": {"shasum":"9c426a1bd55e98718ab4ddcc01fa57ea83c649f1"}, "_from": "npm-test-array-bin/" } test: !!js/function | function(jju, input) { obj = { name: 'npm-test-array-bin', version: '1.2.5', bin: { 'array-bin': 'bin/array-bin' }, scripts: { test: 'node test.js' }, readme: 'just an npm test\n', readmeFilename: 'README', description: 'just an npm test', _id: 'npm-test-array-bin@1.2.5', dist: { shasum: '9c426a1bd55e98718ab4ddcc01fa57ea83c649f1' }, _from: 'npm-test-array-bin/' } return jju.update(input, obj) } jju-1.1.0/test/update/pkg-json5.yaml000066400000000000000000000015001240215127700172400ustar00rootroot00000000000000input: | // vim:syntax=javascript { name: 'yapm', version: '1.1.0-1325', // upstream npm@1.3.25 description: 'A package manager for node (npm fork)', } output: | // vim:syntax=javascript { name: 'yapm', version: '1.1.0-1325', // upstream npm@1.3.25 description: 'A package manager for node (npm fork)', _id: 'yapm@1.1.0-1325', dist: { shasum: 'd5aa31c1ad00c1e7e57e07cea1b22c1806a47111', }, _from: './zzz', } test: !!js/function | function(jju, input) { var upd = { "name": "yapm", "version": "1.1.0-1325", "description": "A package manager for node (npm fork)", "_id": "yapm@1.1.0-1325", "dist": { "shasum": "d5aa31c1ad00c1e7e57e07cea1b22c1806a47111" }, "_from": "./zzz" } return jju.update(input, upd) }