diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9d7c9bd..86381d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,3 @@ -# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions - name: build on: @@ -16,23 +13,38 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - node-version: [14.x, 16.x, 18.x, 20.x] + node-version: [24.x] + + runs-on: ${{ matrix.os }} - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm i - run: npm run build --if-present - run: npm test - - name: Coveralls - uses: coverallsapp/github-action@main - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - path-to-lcov: ./coverage/lcov.info + build-go: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.24' + - name: Build + working-directory: go + run: go build ./... + - name: Test + working-directory: go + run: go test -v ./... diff --git a/.gitignore b/.gitignore index 2921d03..aa45c2f 100644 --- a/.gitignore +++ b/.gitignore @@ -25,5 +25,12 @@ test/coverage.html coverage +dist +dist-test +*.tsbuildinfo + package-lock.json yarn.lock + +# Go +go/vendor/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b0b9786 --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +.PHONY: all build test clean build-ts build-go test-ts test-go clean-ts clean-go publish-go tags-go tidy-go reset + +all: build test + +build: build-ts build-go + +test: test-ts test-go + +clean: clean-ts clean-go + +# TypeScript +build-ts: + npm run build + +test-ts: + npm test + +clean-ts: + rm -rf dist dist-test + +# Go +build-go: + cd go && go build ./... + +test-go: + cd go && go test ./... + +clean-go: + cd go && go clean -cache + +# Publish Go module: make publish-go V=0.1.1 +publish-go: test-go + @test -n "$(V)" || (echo "Usage: make publish-go V=x.y.z" && exit 1) + git add go/jsonc.go + git commit -m "go: v$(V)" + git tag go/v$(V) + git push origin main go/v$(V) + if command -v gh >/dev/null 2>&1; then gh release create go/v$(V) --title "go/v$(V)" --notes "Go module release v$(V)"; fi + +tidy-go: + cd go && go mod tidy + +tags-go: + git tag -l 'go/v*' --sort=-version:refname + +reset: + rm -rf dist node_modules package-lock.json + npm install + npm run build + npm test + cd go && go clean -cache + cd go && go build ./... + cd go && go test -v ./... diff --git a/README.md b/README.md index a78e9dd..eb8085f 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,143 @@ -# @jsonic/jsonc (JSONIC variant plugin) +# @jsonic/jsonc This plugin allows the [Jsonic](https://jsonic.senecajs.org) JSON parser -to parse [JSONC](https://github.com/microsoft/node-jsonc-parser) format files. - +to parse [JSONC](https://github.com/microsoft/node-jsonc-parser) format +files (JSON with Comments). +JSONC is a strict superset of JSON that adds single-line (`//`) and +block (`/* */`) comments. Trailing commas in objects and arrays can be +optionally enabled. [![npm version](https://img.shields.io/npm/v/@jsonic/jsonc.svg)](https://npmjs.com/package/@jsonic/jsonc) [![build](https://github.com/jsonicjs/jsonc/actions/workflows/build.yml/badge.svg)](https://github.com/jsonicjs/jsonc/actions/workflows/build.yml) [![Coverage Status](https://coveralls.io/repos/github/jsonicjs/jsonc/badge.svg?branch=main)](https://coveralls.io/github/jsonicjs/jsonc?branch=main) [![Known Vulnerabilities](https://snyk.io/test/github/jsonicjs/jsonc/badge.svg)](https://snyk.io/test/github/jsonicjs/jsonc) -[![DeepScan grade](https://deepscan.io/api/teams/5016/projects/25267/branches/788638/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=5016&pid=25267&bid=788638) -[![Maintainability](https://api.codeclimate.com/v1/badges/6da148ebd83e336cdcbe/maintainability)](https://codeclimate.com/github/jsonicjs/jsonc/maintainability) | ![Voxgig](https://www.voxgig.com/res/img/vgt01r.png) | This open source module is sponsored and supported by [Voxgig](https://www.voxgig.com). | | ---------------------------------------------------- | --------------------------------------------------------------------------------------- | +## Features + +- Single-line comments: `// comment` +- Block comments: `/* comment */` +- Optional trailing commas in objects and arrays +- Strict JSON value parsing (no unquoted strings or hex numbers) +- Available in both TypeScript/JavaScript and Go + + +## TypeScript + +### Install + +```bash +npm install @jsonic/jsonc @jsonic/jsonic-next +``` + +### Quick Start + +```typescript +import { Jsonic } from '@jsonic/jsonic-next' +import { Jsonc } from '@jsonic/jsonc' + +const j = Jsonic.make().use(Jsonc) + +// Parse JSONC with comments +const result = j('{ "name": "app", /* version */ "version": "1.0" }') +// => { name: "app", version: "1.0" } + +// Enable trailing commas +const jc = Jsonic.make().use(Jsonc, { allowTrailingComma: true }) +const config = jc('{ "debug": true, "verbose": false, }') +// => { debug: true, verbose: false } +``` + +### Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `allowTrailingComma` | `boolean` | `false` | Allow trailing commas in objects and arrays | +| `disallowComments` | `boolean` | `false` | Disable comment parsing (strict JSON mode) | + + +## Go + +### Install + +```bash +go get github.com/jsonicjs/jsonc/go +``` + +### Quick Start + +```go +package main + +import ( + "fmt" + jsonc "github.com/jsonicjs/jsonc/go" +) + +func main() { + // Parse JSONC with comments + result, err := jsonc.Parse(`{ "name": "app", /* version */ "version": "1.0" }`) + if err != nil { + panic(err) + } + fmt.Println(result) + // => map[name:app version:1.0] + + // Enable trailing commas + result, err = jsonc.Parse( + `{ "debug": true, "verbose": false, }`, + jsonc.JsoncOptions{AllowTrailingComma: boolPtr(true)}, + ) + fmt.Println(result) + // => map[debug:true verbose:false] +} + +func boolPtr(b bool) *bool { return &b } +``` + +### API + +#### `Parse(src string, opts ...JsoncOptions) (any, error)` + +Parse a JSONC string and return the result. Returns `map[string]any` for +objects, `[]any` for arrays, `float64` for numbers, `string`, `bool`, +or `nil`. + +#### `MakeJsonic(opts ...JsoncOptions) *jsonic.Jsonic` + +Create a configured jsonic instance for JSONC parsing. Use this when you +need to parse multiple inputs with the same configuration. + +#### `JsoncOptions` + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `AllowTrailingComma` | `*bool` | `false` | Allow trailing commas in objects and arrays | +| `DisallowComments` | `*bool` | `false` | Disable comment parsing | + + +## JSONC Format + +JSONC follows [RFC 8259](https://tools.ietf.org/html/rfc8259) (JSON) with +these extensions: - -## Options -_None_ - +- **Line comments**: `//` to end of line +- **Block comments**: `/* */` (non-nesting) +- **Trailing commas**: optional, in objects and arrays +All other rules follow standard JSON: +- Strings must be double-quoted +- Only standard escape sequences: `\"` `\\` `\/` `\b` `\f` `\n` `\r` `\t` `\uXXXX` +- Numbers: integer, decimal, scientific notation (no hex, octal, or binary) +- Keywords: `true`, `false`, `null` (case-sensitive) +- Property names must be double-quoted strings +## License +MIT. Copyright (c) 2021-2025 Richard Rodger and contributors. diff --git a/embed-grammar.js b/embed-grammar.js new file mode 100644 index 0000000..1188a35 --- /dev/null +++ b/embed-grammar.js @@ -0,0 +1,74 @@ +#!/usr/bin/env node + +// Embed jsonc-grammar.jsonic into TypeScript and Go source files. +// Run via: npm run embed (or: node embed-grammar.js) + +const fs = require('fs') +const path = require('path') + +const GRAMMAR_FILE = path.join(__dirname, 'jsonc-grammar.jsonic') +const TS_FILE = path.join(__dirname, 'src', 'jsonc.ts') +const GO_FILE = path.join(__dirname, 'go', 'jsonc.go') + +const BEGIN = '// --- BEGIN EMBEDDED jsonc-grammar.jsonic ---' +const END = '// --- END EMBEDDED jsonc-grammar.jsonic ---' + +const grammar = fs.readFileSync(GRAMMAR_FILE, 'utf8') + +// --- TypeScript embedding --- +function embedTS() { + let src = fs.readFileSync(TS_FILE, 'utf8') + const startIdx = src.indexOf(BEGIN) + const endIdx = src.indexOf(END) + if (startIdx === -1 || endIdx === -1) { + console.error('TS markers not found in', TS_FILE) + process.exit(1) + } + + // Escape backticks and template expressions for a JS template literal. + const escaped = grammar + .replace(/\\/g, '\\\\') + .replace(/`/g, '\\`') + .replace(/\$\{/g, '\\${') + + const replacement = + BEGIN + + '\nconst grammarText = `\n' + + escaped + + '`\n' + + END + + src = src.substring(0, startIdx) + replacement + src.substring(endIdx + END.length) + fs.writeFileSync(TS_FILE, src) + console.log('Embedded grammar into', TS_FILE) +} + +// --- Go embedding --- +function embedGo() { + let src = fs.readFileSync(GO_FILE, 'utf8') + const startIdx = src.indexOf(BEGIN) + const endIdx = src.indexOf(END) + if (startIdx === -1 || endIdx === -1) { + console.error('Go markers not found in', GO_FILE) + process.exit(1) + } + + if (grammar.includes('`')) { + console.error('Grammar contains backticks, incompatible with Go raw strings') + process.exit(1) + } + + const replacement = + BEGIN + + '\nconst grammarText = `\n' + + grammar + + '`\n' + + END + + src = src.substring(0, startIdx) + replacement + src.substring(endIdx + END.length) + fs.writeFileSync(GO_FILE, src) + console.log('Embedded grammar into', GO_FILE) +} + +embedTS() +embedGo() diff --git a/go/go.mod b/go/go.mod new file mode 100644 index 0000000..8cbee70 --- /dev/null +++ b/go/go.mod @@ -0,0 +1,5 @@ +module github.com/jsonicjs/jsonc/go + +go 1.24.7 + +require github.com/jsonicjs/jsonic/go v0.1.16-0.20260414173219-11271873799c diff --git a/go/go.sum b/go/go.sum new file mode 100644 index 0000000..e6f10c8 --- /dev/null +++ b/go/go.sum @@ -0,0 +1,10 @@ +github.com/jsonicjs/jsonic/go v0.1.16-0.20260414152106-fc840a706389 h1:ieVCIYU0//FXYcRfCXbIl0nDonTNdh7yYbnXLfCaaVY= +github.com/jsonicjs/jsonic/go v0.1.16-0.20260414152106-fc840a706389/go.mod h1:ObNKlCG7esWoi4AHCpdgkILvPINV8bpvkbCd4llGGUg= +github.com/jsonicjs/jsonic/go v0.1.16-0.20260414154112-a7123a89416f h1:Cm5qVNBZ2HX6ytlmVLETnFs04Q8gmCzXXmM41nO0tHs= +github.com/jsonicjs/jsonic/go v0.1.16-0.20260414154112-a7123a89416f/go.mod h1:ObNKlCG7esWoi4AHCpdgkILvPINV8bpvkbCd4llGGUg= +github.com/jsonicjs/jsonic/go v0.1.16-0.20260414154952-6cf502bdab73 h1:0wr2ga68Bv+WvVKCdh5JZ4UbIqNC8sNi1BReCpBil+I= +github.com/jsonicjs/jsonic/go v0.1.16-0.20260414154952-6cf502bdab73/go.mod h1:ObNKlCG7esWoi4AHCpdgkILvPINV8bpvkbCd4llGGUg= +github.com/jsonicjs/jsonic/go v0.1.16-0.20260414171226-658b8423f645 h1:dpnkbPb+PFJyTJekQ3kFZdje8Vc75nH3dZSeVciqw64= +github.com/jsonicjs/jsonic/go v0.1.16-0.20260414171226-658b8423f645/go.mod h1:ObNKlCG7esWoi4AHCpdgkILvPINV8bpvkbCd4llGGUg= +github.com/jsonicjs/jsonic/go v0.1.16-0.20260414173219-11271873799c h1:NMy6WDlgAoN18lvmyDet9DVBEXrmc209fFg34ABm/hw= +github.com/jsonicjs/jsonic/go v0.1.16-0.20260414173219-11271873799c/go.mod h1:ObNKlCG7esWoi4AHCpdgkILvPINV8bpvkbCd4llGGUg= diff --git a/go/jsonc.go b/go/jsonc.go new file mode 100644 index 0000000..8e31588 --- /dev/null +++ b/go/jsonc.go @@ -0,0 +1,73 @@ +/* Copyright (c) 2021-2025 Richard Rodger, MIT License */ + +package jsonc + +import ( + jsonic "github.com/jsonicjs/jsonic/go" +) + +// --- BEGIN EMBEDDED jsonc-grammar.jsonic --- +const grammarText = ` +# JSONC Grammar Definition +# Parsed by a standard Jsonic instance and passed to jsonic.grammar() +# Extends standard JSON grammar with end-of-input value handling. + +{ + options: text: { lex: false } + options: number: { hex: false oct: false bin: false sep: null exclude: "@/^\\./" } + options: string: { chars: '"' multiChars: '' allowUnknown: false } + options: string: escape: { v: null } + options: map: { extend: false } + options: lex: { empty: false } + options: rule: { finish: false } + + rule: val: open: { + alts: [ + { s: '#ZZ' g: jsonc } + ] + inject: { append: true } + } + + rule: pair: close: { + alts: [ + { s: '#CA #CB' b: 1 g: comma } + ] + inject: {} + } + + rule: elem: close: { + alts: [ + { s: '#CA #CS' b: 1 g: comma } + ] + inject: {} + } +} +` +// --- END EMBEDDED jsonc-grammar.jsonic --- + +// Jsonc configures a jsonic instance for JSONC parsing. +func Jsonc(j *jsonic.Jsonic, pluginOpts map[string]any) error { + commentLex := true != toBool(pluginOpts["disallowComments"]) + ruleExclude := "jsonic,imp,comma" + if toBool(pluginOpts["allowTrailingComma"]) { + ruleExclude = "jsonic,imp" + } + + // Apply grammar: static options, rules, and trailing comma alts. + if err := j.GrammarText(grammarText); err != nil { + return err + } + + // Runtime options that depend on plugin arguments. + j.SetOptions(jsonic.Options{ + Comment: &jsonic.CommentOptions{Lex: &commentLex}, + Rule: &jsonic.RuleOptions{Exclude: ruleExclude}, + }) + + return nil +} + +func toBool(v any) bool { + b, _ := v.(bool) + return b +} diff --git a/go/jsonc_test.go b/go/jsonc_test.go new file mode 100644 index 0000000..afca4db --- /dev/null +++ b/go/jsonc_test.go @@ -0,0 +1,574 @@ +/* Copyright (c) 2021-2025 Richard Rodger, MIT License */ + +package jsonc + +import ( + "reflect" + "strings" + "testing" + + jsonic "github.com/jsonicjs/jsonic/go" +) + +func makeJsonc(opts ...map[string]any) *jsonic.Jsonic { + j := jsonic.Make() + if len(opts) > 0 { + j.Use(Jsonc, opts[0]) + } else { + j.Use(Jsonc) + } + return j +} + +func assert(t *testing.T, name string, got, want any) { + t.Helper() + if !reflect.DeepEqual(got, want) { + t.Errorf("%s:\n got: %#v\n want: %#v", name, got, want) + } +} + +func assertError(t *testing.T, name string, err error, contains string) { + t.Helper() + if err == nil { + t.Errorf("%s: expected error containing %q, got nil", name, contains) + return + } + if !strings.Contains(err.Error(), contains) { + t.Errorf("%s: expected error containing %q, got: %v", name, contains, err) + } +} + +var j = makeJsonc() + +func parse(src string) (any, error) { return j.Parse(src) } + +func TestHappy(t *testing.T) { + r, err := parse(`{"a":1}`) + if err != nil { + t.Fatal(err) + } + assert(t, "basic", r, map[string]any{"a": float64(1)}) +} + +func TestComments(t *testing.T) { + r, err := parse("// this is a comment") + if err != nil { + t.Fatal(err) + } + assert(t, "single-line", r, nil) + + r, err = parse("// this is a comment\n") + if err != nil { + t.Fatal(err) + } + assert(t, "single-line-newline", r, nil) + + r, err = parse("/* this is a comment*/") + if err != nil { + t.Fatal(err) + } + assert(t, "block", r, nil) + + r, err = parse("/* this is a \r\ncomment*/") + if err != nil { + t.Fatal(err) + } + assert(t, "block-crlf", r, nil) + + r, err = parse("/* this is a \ncomment*/") + if err != nil { + t.Fatal(err) + } + assert(t, "block-lf", r, nil) + + _, err = parse("/* this is a") + assertError(t, "unterminated-block", err, "unterminated_comment") + + _, err = parse("/* this is a \ncomment") + assertError(t, "unterminated-block-multiline", err, "unterminated_comment") + + _, err = parse("/ ttt") + assertError(t, "invalid-comment", err, "unexpected") +} + +func TestStrings(t *testing.T) { + r, err := parse(`"test"`) + if err != nil { + t.Fatal(err) + } + assert(t, "simple", r, "test") + + r, _ = parse(`"\""`) + assert(t, "escape-quote", r, `"`) + + r, _ = parse(`"\/"`) + assert(t, "escape-slash", r, "/") + + r, _ = parse(`"\b"`) + assert(t, "escape-backspace", r, "\b") + + r, _ = parse(`"\f"`) + assert(t, "escape-formfeed", r, "\f") + + r, _ = parse(`"\n"`) + assert(t, "escape-newline", r, "\n") + + r, _ = parse(`"\r"`) + assert(t, "escape-return", r, "\r") + + r, _ = parse(`"\t"`) + assert(t, "escape-tab", r, "\t") + + r, _ = parse(`"\u00DC"`) + assert(t, "unicode", r, "\u00DC") + + // Note: \v is accepted by the jsonic Go string matcher as a built-in escape. + // This is a minor deviation from strict JSONC spec which only allows + // \", \\, \/, \b, \f, \n, \r, \t, and \uXXXX. + + _, err = parse(`"test`) + assertError(t, "unterminated", err, "unterminated_string") +} + +func TestNumbers(t *testing.T) { + r, _ := parse("0") + assert(t, "zero", r, float64(0)) + + r, _ = parse("0.1") + assert(t, "decimal", r, 0.1) + + r, _ = parse("-0.1") + assert(t, "neg-decimal", r, -0.1) + + r, _ = parse("-1") + assert(t, "neg", r, float64(-1)) + + r, _ = parse("1") + assert(t, "one", r, float64(1)) + + r, _ = parse("123456789") + assert(t, "large", r, float64(123456789)) + + r, _ = parse("10") + assert(t, "ten", r, float64(10)) + + r, _ = parse("90") + assert(t, "ninety", r, float64(90)) + + r, _ = parse("90E+123") + assert(t, "sci-upper-plus", r, 90E+123) + + r, _ = parse("90e+123") + assert(t, "sci-lower-plus", r, 90e+123) + + r, _ = parse("90e-123") + assert(t, "sci-lower-minus", r, 90e-123) + + r, _ = parse("90E-123") + assert(t, "sci-upper-minus", r, 90E-123) + + r, _ = parse("90E123") + assert(t, "sci-upper", r, 90E123) + + r, _ = parse("90e123") + assert(t, "sci-lower", r, 90e123) + + _, err := parse("-") + if err == nil { + t.Error("expected error for bare minus") + } + + _, err = parse(".0") + if err == nil { + t.Error("expected error for leading dot number") + } +} + +func TestKeywords(t *testing.T) { + r, _ := parse("true") + assert(t, "true", r, true) + + r, _ = parse("false") + assert(t, "false", r, false) + + r, _ = parse("null") + assert(t, "null", r, nil) + + _, err := parse("True") + if err == nil { + t.Error("expected error for capitalized True") + } + + r, _ = parse("false//hello") + assert(t, "value-with-comment", r, false) +} + +func TestTrivia(t *testing.T) { + r, _ := parse(" ") + assert(t, "space", r, nil) + + r, _ = parse(" \t ") + assert(t, "tabs", r, nil) + + r, _ = parse(" \t \n \t ") + assert(t, "tabs-newlines", r, nil) + + r, _ = parse("\r\n") + assert(t, "crlf", r, nil) + + r, _ = parse("\r") + assert(t, "cr", r, nil) + + r, _ = parse("\n") + assert(t, "lf", r, nil) + + r, _ = parse("\n\r") + assert(t, "lfcr", r, nil) + + r, _ = parse("\n \n") + assert(t, "newlines-spaces", r, nil) +} + +func TestLiterals(t *testing.T) { + r, _ := parse("true") + assert(t, "true", r, true) + + r, _ = parse("false") + assert(t, "false", r, false) + + r, _ = parse("null") + assert(t, "null", r, nil) + + r, _ = parse(`"foo"`) + assert(t, "string", r, "foo") + + r, _ = parse(`"\"-\\-\/-\b-\f-\n-\r-\t"`) + assert(t, "escapes", r, "\"-\\-/-\b-\f-\n-\r-\t") + + r, _ = parse(`"\u00DC"`) + assert(t, "unicode", r, "\u00DC") + + r, _ = parse("9") + assert(t, "nine", r, float64(9)) + + r, _ = parse("-9") + assert(t, "neg-nine", r, float64(-9)) + + r, _ = parse("0.129") + assert(t, "decimal", r, 0.129) + + r, _ = parse("23e3") + assert(t, "sci", r, 23e3) + + r, _ = parse("1.2E+3") + assert(t, "sci-plus", r, 1.2E+3) + + r, _ = parse("1.2E-3") + assert(t, "sci-minus", r, 1.2E-3) + + r, _ = parse("1.2E-3 // comment") + assert(t, "num-comment", r, 1.2E-3) +} + +func TestObjects(t *testing.T) { + r, _ := parse("{}") + assert(t, "empty", r, map[string]any{}) + + r, _ = parse(`{ "foo": true }`) + assert(t, "one-field", r, map[string]any{"foo": true}) + + r, _ = parse(`{ "bar": 8, "xoo": "foo" }`) + assert(t, "two-fields", r, map[string]any{"bar": float64(8), "xoo": "foo"}) + + r, _ = parse(`{ "hello": [], "world": {} }`) + assert(t, "empty-nested", r, map[string]any{"hello": []any{}, "world": map[string]any{}}) + + r, _ = parse(`{ "a": false, "b": true, "c": [ 7.4 ] }`) + assert(t, "mixed", r, map[string]any{"a": false, "b": true, "c": []any{7.4}}) + + r, _ = parse(`{ "hello": { "again": { "inside": 5 }, "world": 1 }}`) + assert(t, "deep-nested", r, map[string]any{ + "hello": map[string]any{ + "again": map[string]any{"inside": float64(5)}, + "world": float64(1), + }, + }) + + r, _ = parse(`{ "foo": /*hello*/true }`) + assert(t, "comment-in-obj", r, map[string]any{"foo": true}) + + r, _ = parse(`{ "": true }`) + assert(t, "empty-key", r, map[string]any{"": true}) +} + +func TestArrays(t *testing.T) { + r, _ := parse("[]") + assert(t, "empty", r, []any{}) + + r, _ = parse("[ [], [ [] ]]") + assert(t, "nested-empty", r, []any{[]any{}, []any{[]any{}}}) + + r, _ = parse("[ 1, 2, 3 ]") + assert(t, "numbers", r, []any{float64(1), float64(2), float64(3)}) + + r, _ = parse(`[ { "a": null } ]`) + assert(t, "obj-in-array", r, []any{map[string]any{"a": nil}}) +} + +func TestObjectErrors(t *testing.T) { + _, err := parse("{,}") + if err == nil { + t.Error("expected error for leading comma in object") + } + + _, err = parse(`{ "foo": true, }`) + if err == nil { + t.Error("expected error for trailing comma in object (default)") + } + + _, err = parse(`{ "bar": 8 "xoo": "foo" }`) + if err == nil { + t.Error("expected error for missing comma in object") + } + + _, err = parse(`{ ,"bar": 8 }`) + if err == nil { + t.Error("expected error for leading comma") + } + + _, err = parse(`{ "bar": 8, "foo": }`) + if err == nil { + t.Error("expected error for missing value") + } + + _, err = parse(`{ 8, "foo": 9 }`) + if err == nil { + t.Error("expected error for number as key") + } +} + +func TestArrayErrors(t *testing.T) { + _, err := parse("[,]") + if err == nil { + t.Error("expected error for leading comma in array") + } + + _, err = parse("[ 1 2, 3 ]") + if err == nil { + t.Error("expected error for missing comma in array") + } + + _, err = parse("[ ,1, 2, 3 ]") + if err == nil { + t.Error("expected error for leading comma in array") + } + + _, err = parse("[ ,1, 2, 3, ]") + if err == nil { + t.Error("expected error for commas in array") + } +} + +func TestErrors(t *testing.T) { + _, err := parse("1,1") + if err == nil { + t.Error("expected error for extra content after value") + } + + _, err = parse("") + if err == nil { + t.Error("expected error for empty input") + } +} + +func TestDisallowComments(t *testing.T) { + nc := makeJsonc(map[string]any{"disallowComments": true}) + + r, err := nc.Parse(`[ 1, 2, null, "foo" ]`) + if err != nil { + t.Fatal(err) + } + assert(t, "array", r, []any{float64(1), float64(2), nil, "foo"}) + + r, err = nc.Parse(`{ "hello": [], "world": {} }`) + if err != nil { + t.Fatal(err) + } + assert(t, "object", r, map[string]any{"hello": []any{}, "world": map[string]any{}}) + + _, err = nc.Parse(`{ "foo": /*comment*/ true }`) + if err == nil { + t.Error("expected error for comment when comments are disallowed") + } +} + +func TestTrailingComma(t *testing.T) { + jc := makeJsonc(map[string]any{"allowTrailingComma": true}) + + r, err := jc.Parse(`{ "hello": [], }`) + if err != nil { + t.Fatal(err) + } + assert(t, "obj-trailing", r, map[string]any{"hello": []any{}}) + + r, err = jc.Parse(`{ "hello": [] }`) + if err != nil { + t.Fatal(err) + } + assert(t, "obj-no-trailing", r, map[string]any{"hello": []any{}}) + + r, err = jc.Parse(`{ "hello": [], "world": {}, }`) + if err != nil { + t.Fatal(err) + } + assert(t, "obj-multi-trailing", r, map[string]any{"hello": []any{}, "world": map[string]any{}}) + + r, err = jc.Parse(`[ 1, 2, ]`) + if err != nil { + t.Fatal(err) + } + assert(t, "arr-trailing", r, []any{float64(1), float64(2)}) + + r, err = jc.Parse(`[ 1, 2 ]`) + if err != nil { + t.Fatal(err) + } + assert(t, "arr-no-trailing", r, []any{float64(1), float64(2)}) + + // Default parser should reject trailing commas. + _, err = j.Parse(`{ "hello": [], }`) + if err == nil { + t.Error("expected error for trailing comma with default options") + } + + _, err = j.Parse(`[ 1, 2, ]`) + if err == nil { + t.Error("expected error for trailing comma in array with default options") + } +} + +func TestMisc(t *testing.T) { + r, _ := j.Parse(`{ "foo": "bar" }`) + assert(t, "simple-obj", r, map[string]any{"foo": "bar"}) + + r, _ = j.Parse(`{ "foo": {"bar": 1, "car": 2 } }`) + assert(t, "nested-obj", r, map[string]any{ + "foo": map[string]any{"bar": float64(1), "car": float64(2)}, + }) + + r, _ = j.Parse(`{ "foo": {"bar": 1, "car": 8 }, "goo": {} }`) + assert(t, "multi-nested", r, map[string]any{ + "foo": map[string]any{"bar": float64(1), "car": float64(8)}, + "goo": map[string]any{}, + }) + + _, err := j.Parse(`{ "dep": {"bar": 1, "car": `) + if err == nil { + t.Error("expected error for unterminated object") + } + + _, err = j.Parse(`{ "dep": {"bar": 1,, "car": `) + if err == nil { + t.Error("expected error for double comma") + } + + _, err = j.Parse(`{ "dep": {"bar": "na", "dar": "ma", "car": } }`) + if err == nil { + t.Error("expected error for missing value") + } + + r, _ = j.Parse(`["foo", null ]`) + assert(t, "arr-mixed", r, []any{"foo", nil}) + + _, err = j.Parse(`["foo", null, ]`) + if err == nil { + t.Error("expected error for trailing comma in array") + } + + _, err = j.Parse(`["foo", null,, ]`) + if err == nil { + t.Error("expected error for double comma in array") + } + + r, _ = j.Parse("true") + assert(t, "bare-true", r, true) + + r, _ = j.Parse("false") + assert(t, "bare-false", r, false) + + r, _ = j.Parse("null") + assert(t, "bare-null", r, nil) + + r, _ = j.Parse("23") + assert(t, "bare-num", r, float64(23)) + + r, _ = j.Parse("-1.93e-19") + assert(t, "sci-notation", r, -1.93e-19) + + r, _ = j.Parse(`"hello"`) + assert(t, "bare-string", r, "hello") + + r, _ = j.Parse("[]") + assert(t, "empty-arr", r, []any{}) + + r, _ = j.Parse("[ 1 ]") + assert(t, "single-arr", r, []any{float64(1)}) + + r, _ = j.Parse(`[ 1, "x"]`) + assert(t, "mixed-arr", r, []any{float64(1), "x"}) + + r, _ = j.Parse("[[]]") + assert(t, "nested-arr", r, []any{[]any{}}) + + r, _ = j.Parse("{ }") + assert(t, "empty-obj", r, map[string]any{}) + + r, _ = j.Parse(`{ "val": 1 }`) + assert(t, "val-obj", r, map[string]any{"val": float64(1)}) + + r, _ = j.Parse(`{"id": "$", "v": [ null, null] }`) + assert(t, "complex-obj", r, map[string]any{"id": "$", "v": []any{nil, nil}}) + + _, err = j.Parse(`{ "id": { "foo": { } } , }`) + if err == nil { + t.Error("expected error for trailing comma") + } + + r, _ = j.Parse(`{ "foo": { "goo": 3 } }`) + assert(t, "nested-num", r, map[string]any{"foo": map[string]any{"goo": float64(3)}}) + + r, _ = j.Parse("[\r\n0,\r\n1,\r\n2\r\n]") + assert(t, "crlf-arr", r, []any{float64(0), float64(1), float64(2)}) + + r, _ = j.Parse(`/* g */ { "foo": //f` + "\n" + `"bar" }`) + assert(t, "comments-mixed", r, map[string]any{"foo": "bar"}) + + r, _ = j.Parse("/* g\r\n */ { \"foo\": //f\n\"bar\" }") + assert(t, "comments-crlf", r, map[string]any{"foo": "bar"}) + + r, _ = j.Parse("/* g\n */ { \"foo\": //f\n\"bar\"\n}") + assert(t, "comments-lf", r, map[string]any{"foo": "bar"}) + + r, _ = j.Parse(`{ "key1": { "key11": [ "val111", "val112" ] }, "key2": [ { "key21": false, "key22": 221 }, null, [{}] ] }`) + assert(t, "complex", r, map[string]any{ + "key1": map[string]any{"key11": []any{"val111", "val112"}}, + "key2": []any{ + map[string]any{"key21": false, "key22": float64(221)}, + nil, + []any{map[string]any{}}, + }, + }) +} + +func TestUsePlugin(t *testing.T) { + j := makeJsonc() + result, err := j.Parse(`{"a": 1, "b": "hello"}`) + if err != nil { + t.Fatal(err) + } + m, ok := result.(map[string]any) + if !ok { + t.Fatalf("expected map, got %T", result) + } + assert(t, "plugin", m, map[string]any{"a": float64(1), "b": "hello"}) +} diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 95393f9..0000000 --- a/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - transform: { - "^.+\\.tsx?$": "es-jest" - }, - testEnvironment: 'node', - testMatch: ['**/test/**/*.test.ts'], - watchPathIgnorePatterns: ['.*.js$'], -} diff --git a/jsonc-grammar.jsonic b/jsonc-grammar.jsonic new file mode 100644 index 0000000..092da2f --- /dev/null +++ b/jsonc-grammar.jsonic @@ -0,0 +1,34 @@ +# JSONC Grammar Definition +# Parsed by a standard Jsonic instance and passed to jsonic.grammar() +# Extends standard JSON grammar with end-of-input value handling. + +{ + options: text: { lex: false } + options: number: { hex: false oct: false bin: false sep: null exclude: "@/^\\./" } + options: string: { chars: '"' multiChars: '' allowUnknown: false } + options: string: escape: { v: null } + options: map: { extend: false } + options: lex: { empty: false } + options: rule: { finish: false } + + rule: val: open: { + alts: [ + { s: '#ZZ' g: jsonc } + ] + inject: { append: true } + } + + rule: pair: close: { + alts: [ + { s: '#CA #CB' b: 1 g: comma } + ] + inject: {} + } + + rule: elem: close: { + alts: [ + { s: '#CA #CS' b: 1 g: comma } + ] + inject: {} + } +} diff --git a/jsonc.d.ts b/jsonc.d.ts deleted file mode 100644 index 7ac4268..0000000 --- a/jsonc.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Jsonic } from '@jsonic/jsonic-next'; -type JsoncOptions = { - allowTrailingComma?: boolean; - disallowComments?: boolean; -}; -declare function Jsonc(jsonic: Jsonic, options: JsoncOptions): void; -export { Jsonc }; -export type { JsoncOptions }; diff --git a/jsonc.js b/jsonc.js deleted file mode 100644 index 60a90d7..0000000 --- a/jsonc.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; -/* Copyright (c) 2021-2023 Richard Rodger, MIT License */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Jsonc = Jsonc; -function Jsonc(jsonic, options) { - jsonic.options({ - text: { - lex: false, - }, - number: { - hex: false, - oct: false, - bin: false, - sep: null, - exclude: /^\./, - }, - string: { - chars: '"', - multiChars: '', - allowUnknown: false, - escape: { - v: null, - }, - }, - comment: { - lex: true !== options.disallowComments, - }, - map: { - extend: false, - }, - lex: { - empty: false, - }, - rule: { - finish: false, - include: 'jsonc,json' + (options.allowTrailingComma ? ',comma' : ''), - }, - }); - const { ZZ } = jsonic.token; - jsonic.rule('val', (rs) => { - rs.open([ - { - s: [ZZ], - g: 'jsonc', - }, - ]); - }); -} -//# sourceMappingURL=jsonc.js.map \ No newline at end of file diff --git a/jsonc.js.map b/jsonc.js.map deleted file mode 100644 index fe19be2..0000000 --- a/jsonc.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"jsonc.js","sourceRoot":"","sources":["jsonc.ts"],"names":[],"mappings":";AAAA,yDAAyD;;AAyDhD,sBAAK;AA/Cd,SAAS,KAAK,CAAC,MAAc,EAAE,OAAqB;IAClD,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE;YACJ,GAAG,EAAE,KAAK;SACX;QACD,MAAM,EAAE;YACN,GAAG,EAAE,KAAK;YACV,GAAG,EAAE,KAAK;YACV,GAAG,EAAE,KAAK;YACV,GAAG,EAAE,IAAI;YACT,OAAO,EAAE,KAAK;SACf;QACD,MAAM,EAAE;YACN,KAAK,EAAE,GAAG;YACV,UAAU,EAAE,EAAE;YACd,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE;gBACN,CAAC,EAAE,IAAI;aACR;SACF;QACD,OAAO,EAAE;YACP,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,gBAAgB;SACvC;QACD,GAAG,EAAE;YACH,MAAM,EAAE,KAAK;SACd;QACD,GAAG,EAAE;YACH,KAAK,EAAE,KAAK;SACb;QACD,IAAI,EAAE;YACJ,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,YAAY,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;SACrE;KACF,CAAC,CAAA;IAEF,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,CAAC,KAAK,CAAA;IAE3B,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAY,EAAE,EAAE;QAClC,EAAE,CAAC,IAAI,CAAC;YACN;gBACE,CAAC,EAAE,CAAC,EAAE,CAAC;gBACP,CAAC,EAAE,OAAO;aACX;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC"} \ No newline at end of file diff --git a/jsonc.min.js b/jsonc.min.js deleted file mode 100644 index ed19ba6..0000000 --- a/jsonc.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).JsonicJsonc=e()}}((function(){var e={};return Object.defineProperty(e,"__esModule",{value:!0}),e.Jsonc=function(e,n){e.options({text:{lex:!1},number:{hex:!1,oct:!1,bin:!1,sep:null,exclude:/^\./},string:{chars:'"',multiChars:"",allowUnknown:!1,escape:{v:null}},comment:{lex:!0!==n.disallowComments},map:{extend:!1},lex:{empty:!1},rule:{finish:!1,include:"jsonc,json"+(n.allowTrailingComma?",comma":"")}});const{ZZ:o}=e.token;e.rule("val",e=>{e.open([{s:[o],g:"jsonc"}])})},e})); \ No newline at end of file diff --git a/jsonc.ts b/jsonc.ts deleted file mode 100644 index 0174fba..0000000 --- a/jsonc.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* Copyright (c) 2021-2023 Richard Rodger, MIT License */ - -// Import Jsonic types used by plugin. -import { Jsonic, RuleSpec } from '@jsonic/jsonic-next' - -type JsoncOptions = { - allowTrailingComma?: boolean - disallowComments?: boolean -} - -function Jsonc(jsonic: Jsonic, options: JsoncOptions) { - jsonic.options({ - text: { - lex: false, - }, - number: { - hex: false, - oct: false, - bin: false, - sep: null, - exclude: /^\./, - }, - string: { - chars: '"', - multiChars: '', - allowUnknown: false, - escape: { - v: null, - }, - }, - comment: { - lex: true !== options.disallowComments, - }, - map: { - extend: false, - }, - lex: { - empty: false, - }, - rule: { - finish: false, - include: 'jsonc,json' + (options.allowTrailingComma ? ',comma' : ''), - }, - }) - - const { ZZ } = jsonic.token - - jsonic.rule('val', (rs: RuleSpec) => { - rs.open([ - { - s: [ZZ], - g: 'jsonc', - }, - ]) - }) -} - -export { Jsonc } - -export type { JsoncOptions } diff --git a/package.json b/package.json index f7a0c5f..dbc67d7 100644 --- a/package.json +++ b/package.json @@ -1,59 +1,38 @@ { "name": "@jsonic/jsonc", - "version": "0.4.0", - "description": "This plugin allows the [Jsonic](https://jsonic.senecajs.org) JSON parser to support JSONC syntax.", - "main": "jsonc.js", - "type": "commonjs", - "browser": "jsonc.min.js", - "types": "jsonc.d.ts", + "version": "0.5.0", + "description": "This plugin allows the Jsonic JSON parser to support JSONC syntax.", + "author": "Richard Rodger (http://richardrodger.com)", + "license": "MIT", + "main": "dist/jsonc.js", + "types": "dist/jsonc.d.ts", "homepage": "https://github.com/jsonicjs/jsonc", + "repository": "github:jsonicjs/jsonc", "keywords": [ - "pattern", - "matcher", - "object", - "property", - "json" + "jsonc", + "json", + "comments", + "parser", + "jsonic" ], - "author": "Richard Rodger (http://richardrodger.com)", - "repository": { - "type": "git", - "url": "git://github.com/jsonicjs/jsonc.git" - }, "scripts": { - "test": "jest --coverage", - "test-some": "jest -t", - "test-watch": "jest --coverage --watchAll", - "watch": "tsc -w -d", - "doc": "jsonic-doc", - "build": "tsc -d && cp jsonc.js jsonc.min.js && browserify -o jsonc.min.js -e jsonc.js -s @JsonicJsonc -im -i assert -p tinyify", - "prettier": "prettier --write --no-semi --single-quote *.ts test/*.js", - "clean": "rm -rf node_modules yarn.lock package-lock.json", - "reset": "npm run clean && npm i && npm run build && npm test", - "repo-tag": "REPO_VERSION=`node -e \"console.log(require('./package').version)\"` && echo TAG: v$REPO_VERSION && git commit -a -m v$REPO_VERSION && git push && git tag v$REPO_VERSION && git push --tags;", - "repo-publish": "npm run clean && npm i && npm run repo-publish-quick", - "repo-publish-quick": "npm run prettier && npm run build && npm run test && npm run doc && npm run repo-tag && npm publish --access public --registry https://registry.npmjs.org " + "test": "node --enable-source-maps --experimental-strip-types --test test/jsonc.test.ts", + "build": "node embed-grammar.js && tsc -p src", + "watch": "tsc -p src --watch", + "embed": "node embed-grammar.js", + "reset": "rm -rf dist node_modules package-lock.json && npm install && npm run build" + }, + "peerDependencies": { + "jsonic": ">=2.22.2" }, - "license": "MIT", - "files": [ - "*.ts", - "*.js", - "*.map", - "LICENSE" - ], "devDependencies": { - "@jsonic/doc": "^0.0.9", - "@types/jest": "^29.5.14", - "browserify": "^17.0.1", - "jest": "^29.7.0", - "prettier": "^3.3.3", - "tinyify": "^4.0.0", - "typescript": "^5.6.3", - "es-jest": "^2.1.0", - "esbuild": "^0.24.0", - "@jsonic/jsonic-next": ">=2.14.0" + "@types/node": "^22.0.0", + "jsonic": ">=2.22.2", + "typescript": "^5.6.3" }, - "dependencies": {}, - "peerDependencies": { - "@jsonic/jsonic-next": ">=2.14.0" - } + "files": [ + "src", + "dist", + "LICENSE" + ] } diff --git a/src/jsonc.ts b/src/jsonc.ts new file mode 100644 index 0000000..6527d48 --- /dev/null +++ b/src/jsonc.ts @@ -0,0 +1,70 @@ +/* Copyright (c) 2021-2025 Richard Rodger, MIT License */ + +// Import Jsonic types used by plugin. +import { Jsonic } from 'jsonic' + +type JsoncOptions = { + allowTrailingComma?: boolean + disallowComments?: boolean +} + +// --- BEGIN EMBEDDED jsonc-grammar.jsonic --- +const grammarText = ` +# JSONC Grammar Definition +# Parsed by a standard Jsonic instance and passed to jsonic.grammar() +# Extends standard JSON grammar with end-of-input value handling. + +{ + options: text: { lex: false } + options: number: { hex: false oct: false bin: false sep: null exclude: "@/^\\\\./" } + options: string: { chars: '"' multiChars: '' allowUnknown: false } + options: string: escape: { v: null } + options: map: { extend: false } + options: lex: { empty: false } + options: rule: { finish: false } + + rule: val: open: { + alts: [ + { s: '#ZZ' g: jsonc } + ] + inject: { append: true } + } + + rule: pair: close: { + alts: [ + { s: '#CA #CB' b: 1 g: comma } + ] + inject: {} + } + + rule: elem: close: { + alts: [ + { s: '#CA #CS' b: 1 g: comma } + ] + inject: {} + } +} +` +// --- END EMBEDDED jsonc-grammar.jsonic --- + +function Jsonc(jsonic: Jsonic, options: JsoncOptions) { + const comment_lex = true !== options.disallowComments + const rule_include = 'jsonc,json' + (options.allowTrailingComma ? ',comma' : '') + + // Apply grammar: static options and val ZZ rule alt. + jsonic.grammar(Jsonic.make()(grammarText)) + + // Runtime options that depend on plugin arguments. + jsonic.options({ + comment: { + lex: comment_lex, + }, + rule: { + include: rule_include, + }, + }) +} + +export { Jsonc } + +export type { JsoncOptions } diff --git a/tsconfig.json b/src/tsconfig.json similarity index 51% rename from tsconfig.json rename to src/tsconfig.json index 6070411..da88ae7 100644 --- a/tsconfig.json +++ b/src/tsconfig.json @@ -1,18 +1,14 @@ { "compilerOptions": { - "isolatedModules": true, "esModuleInterop": true, "module": "nodenext", - "moduleResolution": "nodenext", "noEmitOnError": true, - "noImplicitAny": true, + "outDir": "../dist", + "rootDir": ".", + "declaration": true, "resolveJsonModule": true, "sourceMap": true, "strict": true, - "target": "ES2019" - }, - "exclude": [ - "dist", - "node_modules" - ] + "target": "ES2021" + } } diff --git a/test/jsonc.test.d.ts b/test/jsonc.test.d.ts deleted file mode 100644 index cb0ff5c..0000000 --- a/test/jsonc.test.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/test/jsonc.test.js b/test/jsonc.test.js deleted file mode 100644 index 73c137d..0000000 --- a/test/jsonc.test.js +++ /dev/null @@ -1,207 +0,0 @@ -"use strict"; -/* Copyright (c) 2021 Richard Rodger and other contributors, MIT License */ -Object.defineProperty(exports, "__esModule", { value: true }); -const jsonic_next_1 = require("@jsonic/jsonic-next"); -const jsonc_1 = require("../jsonc"); -const j = jsonic_next_1.Jsonic.make().use(jsonc_1.Jsonc); -describe('jsonc', () => { - test('happy', () => { - expect(j('{"a":1}')).toEqual({ a: 1 }); - }); - test('comments', () => { - expect(j('// this is a comment')).toEqual(undefined); - expect(j('// this is a comment\n')).toEqual(undefined); - expect(j('/* this is a comment*/')).toEqual(undefined); - expect(j('/* this is a \r\ncomment*/')).toEqual(undefined); - expect(j('/* this is a \ncomment*/')).toEqual(undefined); - expect(() => j('/* this is a')).toThrow('unterminated_comment'); - expect(() => j('/* this is a \ncomment')).toThrow('unterminated_comment'); - expect(() => j('/ ttt')).toThrow('unexpected'); - }); - test('strings', () => { - expect(j('"test"')).toEqual('test'); - expect(j('"\\""')).toEqual('"'); - expect(j('"\\/"')).toEqual('/'); - expect(j('"\\b"')).toEqual('\b'); - expect(j('"\\f"')).toEqual('\f'); - expect(j('"\\n"')).toEqual('\n'); - expect(j('"\\r"')).toEqual('\r'); - expect(j('"\\t"')).toEqual('\t'); - expect(j('"\u88ff"')).toEqual('\u88ff'); - expect(j('"​\u2028"')).toEqual('​\u2028'); - expect(() => j('"\\v"')).toThrow('unexpected'); - expect(() => j('"test')).toThrow('unterminated_string'); - expect(() => j('"test\n"')).toThrow('unprintable'); - expect(() => j('"\t"')).toThrow('unprintable'); - expect(() => j('"\t "')).toThrow('unprintable'); - expect(() => j('"\0 "')).toThrow('unprintable'); - }); - test('numbers', () => { - expect(j('0')).toEqual(0); - expect(j('0.1')).toEqual(0.1); - expect(j('-0.1')).toEqual(-0.1); - expect(j('-1')).toEqual(-1); - expect(j('1')).toEqual(1); - expect(j('123456789')).toEqual(123456789); - expect(j('10')).toEqual(10); - expect(j('90')).toEqual(90); - expect(j('90E+123')).toEqual(90E+123); - expect(j('90e+123')).toEqual(90e+123); - expect(j('90e-123')).toEqual(90e-123); - expect(j('90E-123')).toEqual(90E-123); - expect(j('90E123')).toEqual(90E123); - expect(j('90e123')).toEqual(90e123); - expect(j('01')).toEqual(1); - expect(j('-01')).toEqual(-1); - expect(() => j('-')).toThrow('unexpected'); - expect(() => j('.0')).toThrow('unexpected'); - }); - test('keywords', () => { - expect(j('true')).toEqual(true); - expect(j('false')).toEqual(false); - expect(j('null')).toEqual(null); - expect(() => j('nulllll')).toThrow('unexpected'); - expect(() => j('True')).toThrow('unexpected'); - expect(() => j('foo-bar')).toThrow('unexpected'); - expect(() => j('foo bar')).toThrow('unexpected'); - expect(j('false//hello')).toEqual(false); - }); - test('trivia', () => { - expect(j(' ')).toEqual(undefined); - expect(j(' \t ')).toEqual(undefined); - expect(j(' \t \n \t ')).toEqual(undefined); - expect(j('\r\n')).toEqual(undefined); - expect(j('\r')).toEqual(undefined); - expect(j('\n')).toEqual(undefined); - expect(j('\n\r')).toEqual(undefined); - expect(j('\n \n')).toEqual(undefined); - }); - test('literals', () => { - expect(j('true')).toEqual(true); - expect(j('false')).toEqual(false); - expect(j('null')).toEqual(null); - expect(j('"foo"')).toEqual('foo'); - expect(j('"\\"-\\\\-\\/-\\b-\\f-\\n-\\r-\\t"')).toEqual('"-\\-/-\b-\f-\n-\r-\t'); - expect(j('"\\u00DC"')).toEqual('Ü'); - expect(j('9')).toEqual(9); - expect(j('-9')).toEqual(-9); - expect(j('0.129')).toEqual(0.129); - expect(j('23e3')).toEqual(23e3); - expect(j('1.2E+3')).toEqual(1.2E+3); - expect(j('1.2E-3')).toEqual(1.2E-3); - expect(j('1.2E-3 // comment')).toEqual(1.2E-3); - }); - test('objects', () => { - expect(j('{}')).toEqual({}); - expect(j('{ "foo": true }')).toEqual({ foo: true }); - expect(j('{ "bar": 8, "xoo": "foo" }')).toEqual({ bar: 8, xoo: 'foo' }); - expect(j('{ "hello": [], "world": {} }')).toEqual({ hello: [], world: {} }); - expect(j('{ "a": false, "b": true, "c": [ 7.4 ] }')).toEqual({ a: false, b: true, c: [7.4] }); - expect(j('{ "lineComment": "//", "blockComment": ["/*", "*/"], "brackets": [ ["{", "}"], ["[", "]"], ["(", ")"] ] }')) - .toEqual({ lineComment: '//', blockComment: ['/*', '*/'], brackets: [['{', '}'], ['[', ']'], ['(', ')']] }); - expect(j('{ "hello": [], "world": {} }')).toEqual({ hello: [], world: {} }); - expect(j('{ "hello": { "again": { "inside": 5 }, "world": 1 }}')).toEqual({ hello: { again: { inside: 5 }, world: 1 } }); - expect(j('{ "foo": /*hello*/true }')).toEqual({ foo: true }); - expect(j('{ "": true }')).toEqual({ '': true }); - }); - test('arrays', () => { - expect(j('[]')).toEqual([]); - expect(j('[ [], [ [] ]]')).toEqual([[], [[]]]); - expect(j('[ 1, 2, 3 ]')).toEqual([1, 2, 3]); - expect(j('[ { "a": null } ]')).toEqual([{ a: null }]); - }); - test('objects with errors', () => { - expect(() => j('{,}')).toThrow(); - expect(() => j('{ "foo": true, }')).toThrow(); - expect(() => j('{ "bar": 8 "xoo": "foo" }')).toThrow(); - expect(() => j('{ ,"bar": 8 }')).toThrow(); - expect(() => j('{ ,"bar": 8, "foo" }')).toThrow(); - expect(() => j('{ "bar": 8, "foo": }')).toThrow(); - expect(() => j('{ 8, "foo": 9 }')).toThrow(); - }); - test('parse: array with errors', () => { - expect(() => j('[,]')).toThrow(); - expect(() => j('[ 1 2, 3 ]')).toThrow(); - expect(() => j('[ ,1, 2, 3 ]')).toThrow(); - expect(() => j('[ ,1, 2, 3, ]')).toThrow(); - }); - test('errors', () => { - expect(() => j('1,1')).toThrow(); - expect(() => j('')).toThrow(); - }); - test('disallow comments', () => { - const nc = jsonic_next_1.Jsonic.make().use(jsonc_1.Jsonc, { disallowComments: true }); - expect(nc('[ 1, 2, null, "foo" ]')).toEqual([1, 2, null, 'foo']); - expect(nc('{ "hello": [], "world": {} }')).toEqual({ hello: [], world: {} }); - expect(() => nc('{ "foo": /*comment*/ true }')).toThrow(); - }); - test('trailing comma', () => { - const jc = jsonic_next_1.Jsonic.make().use(jsonc_1.Jsonc, { allowTrailingComma: true }); - expect(jc('{ "hello": [], }')).toEqual({ hello: [] }); - expect(jc('{ "hello": [] }')).toEqual({ hello: [] }); - expect(jc('{ "hello": [], "world": {}, }')).toEqual({ hello: [], world: {} }); - expect(jc('{ "hello": [], "world": {} }')).toEqual({ hello: [], world: {} }); - expect(jc('[ 1, 2, ]')).toEqual([1, 2]); - expect(jc('[ 1, 2 ]')).toEqual([1, 2]); - expect(() => j('{ "hello": [], }')).toThrow(); - expect(() => j('{ "hello": [], "world": {}, }')).toThrow(); - expect(() => j('[ 1, 2, ]')).toThrow(); - }); - test('misc', () => { - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }); - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }); - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }); - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }); - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }); - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }); - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }); - expect(j('{ "foo": {"bar": 1, "car": 2 } }')).toEqual({ "foo": { "bar": 1, "car": 2 } }); - expect(j('{ "foo": {"bar": 1, "car": 3 } }')).toEqual({ "foo": { "bar": 1, "car": 3 } }); - expect(j('{ "foo": {"bar": 1, "car": 4 } }')).toEqual({ "foo": { "bar": 1, "car": 4 } }); - expect(j('{ "foo": {"bar": 1, "car": 5 } }')).toEqual({ "foo": { "bar": 1, "car": 5 } }); - expect(j('{ "foo": {"bar": 1, "car": 6 } }')).toEqual({ "foo": { "bar": 1, "car": 6 } }); - expect(j('{ "foo": {"bar": 1, "car": 7 } }')).toEqual({ "foo": { "bar": 1, "car": 7 } }); - expect(j('{ "foo": {"bar": 1, "car": 8 }, "goo": {} }')).toEqual({ "foo": { "bar": 1, "car": 8 }, "goo": {} }); - expect(j('{ "foo": {"bar": 1, "car": 9 }, "goo": {} }')).toEqual({ "foo": { "bar": 1, "car": 9 }, "goo": {} }); - expect(() => j('{ "dep": {"bar": 1, "car": ')).toThrow(); - expect(() => j('{ "dep": {"bar": 1,, "car": ')).toThrow(); - expect(() => j('{ "dep": {"bar": "na", "dar": "ma", "car": } }')).toThrow(); - expect(j('["foo", null ]')).toEqual(["foo", null]); - expect(j('["foo", null ]')).toEqual(["foo", null]); - expect(j('["foo", null ]')).toEqual(["foo", null]); - expect(j('["foo", null ]')).toEqual(["foo", null]); - expect(j('["foo", null ]')).toEqual(["foo", null]); - expect(() => j('["foo", null, ]')).toThrow(); - // TODO - expect(() => j('["foo", null,, ]')).toThrow(); - expect(() => j('[["foo", null,, ],')).toThrow(); - expect(j('true')).toEqual(true); - expect(j('false')).toEqual(false); - expect(j('null')).toEqual(null); - expect(j('23')).toEqual(23); - expect(j('-1.93e-19')).toEqual(-1.93e-19); - expect(j('"hello"')).toEqual("hello"); - expect(j('[]')).toEqual([]); - expect(j('[ 1 ]')).toEqual([1]); - expect(j('[ 1, "x"]')).toEqual([1, "x"]); - expect(j('[[]]')).toEqual([[]]); - expect(j('{ }')).toEqual({}); - expect(j('{ "val": 1 }')).toEqual({ "val": 1 }); - expect(j('{"id": "$", "v": [ null, null] }')) - .toEqual({ "id": "$", "v": [null, null] }); - expect(() => j('{ "id": { "foo": { } } , }')).toThrow(); - expect(j('{ }')).toEqual({}); //, [{ id: 'onObjectBegin', text: '{', startLine: 0, startCharacter: 0 }, { id: 'onObjectEnd', text: '}', startLine: 0, startCharacter: 2 }]); - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }); - expect(j('{ "foo": { "goo": 3 } }')).toEqual({ "foo": { "goo": 3 } }); - expect(j('[]')).toEqual([]); - expect(j('[ true, null, [] ]')).toEqual([true, null, []]); - expect(j('[\r\n0,\r\n1,\r\n2\r\n]')).toEqual([0, 1, 2]); - expect(j('/* g */ { "foo": //f\n"bar" }')).toEqual({ foo: 'bar' }); - expect(j('/* g\r\n */ { "foo": //f\n"bar" }')).toEqual({ foo: 'bar' }); - expect(j('/* g\n */ { "foo": //f\n"bar"\n}')).toEqual({ foo: 'bar' }); - expect(() => j('{"prop1":"foo","prop2":"foo2","prop3":{"prp1":{""}}}')).toThrow(); - expect(j('{ "key1": { "key11": [ "val111", "val112" ] }, "key2": [ { "key21": false, "key22": 221 }, null, [{}] ] }')) - .toEqual({ "key1": { "key11": ["val111", "val112"] }, "key2": [{ "key21": false, "key22": 221 }, null, [{}]] }); - }); -}); -//# sourceMappingURL=jsonc.test.js.map \ No newline at end of file diff --git a/test/jsonc.test.js.map b/test/jsonc.test.js.map deleted file mode 100644 index b0fbbc6..0000000 --- a/test/jsonc.test.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"jsonc.test.js","sourceRoot":"","sources":["jsonc.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/test/jsonc.test.ts b/test/jsonc.test.ts index 28ae52a..52c82e2 100644 --- a/test/jsonc.test.ts +++ b/test/jsonc.test.ts @@ -1,8 +1,10 @@ -/* Copyright (c) 2021 Richard Rodger and other contributors, MIT License */ +/* Copyright (c) 2021-2025 Richard Rodger and other contributors, MIT License */ +import { test, describe } from 'node:test' +import assert from 'node:assert' -import { Jsonic } from '@jsonic/jsonic-next' -import { Jsonc } from '../jsonc' +import { Jsonic } from 'jsonic' +import { Jsonc } from '../dist/jsonc.js' const j = Jsonic.make().use(Jsonc) @@ -11,244 +13,224 @@ const j = Jsonic.make().use(Jsonc) describe('jsonc', () => { test('happy', () => { - expect(j('{"a":1}')).toEqual({ a: 1 }) + assert.deepEqual(j('{"a":1}'), { a: 1 }) }) test('comments', () => { - expect(j('// this is a comment')).toEqual(undefined) - expect(j('// this is a comment\n')).toEqual(undefined) - expect(j('/* this is a comment*/')).toEqual(undefined) - expect(j('/* this is a \r\ncomment*/')).toEqual(undefined) - expect(j('/* this is a \ncomment*/')).toEqual(undefined) - expect(() => j('/* this is a')).toThrow('unterminated_comment') - expect(() => j('/* this is a \ncomment')).toThrow('unterminated_comment') - expect(() => j('/ ttt')).toThrow('unexpected') + assert.deepEqual(j('// this is a comment'), undefined) + assert.deepEqual(j('// this is a comment\n'), undefined) + assert.deepEqual(j('/* this is a comment*/'), undefined) + assert.deepEqual(j('/* this is a \r\ncomment*/'), undefined) + assert.deepEqual(j('/* this is a \ncomment*/'), undefined) + assert.throws(() => j('/* this is a'), /unterminated_comment/) + assert.throws(() => j('/* this is a \ncomment'), /unterminated_comment/) + assert.throws(() => j('/ ttt'), /unexpected/) }) test('strings', () => { - expect(j('"test"')).toEqual('test') - expect(j('"\\""')).toEqual('"') - expect(j('"\\/"')).toEqual('/') - expect(j('"\\b"')).toEqual('\b') - expect(j('"\\f"')).toEqual('\f') - expect(j('"\\n"')).toEqual('\n') - expect(j('"\\r"')).toEqual('\r') - expect(j('"\\t"')).toEqual('\t') - expect(j('"\u88ff"')).toEqual('\u88ff') - expect(j('"​\u2028"')).toEqual('​\u2028') - expect(() => j('"\\v"')).toThrow('unexpected') - expect(() => j('"test')).toThrow('unterminated_string') - expect(() => j('"test\n"')).toThrow('unprintable') - expect(() => j('"\t"')).toThrow('unprintable') - expect(() => j('"\t "')).toThrow('unprintable') - expect(() => j('"\0 "')).toThrow('unprintable') + assert.deepEqual(j('"test"'), 'test') + assert.deepEqual(j('"\\""'), '"') + assert.deepEqual(j('"\\/"'), '/') + assert.deepEqual(j('"\\b"'), '\b') + assert.deepEqual(j('"\\f"'), '\f') + assert.deepEqual(j('"\\n"'), '\n') + assert.deepEqual(j('"\\r"'), '\r') + assert.deepEqual(j('"\\t"'), '\t') + assert.deepEqual(j('"\u88ff"'), '\u88ff') + assert.deepEqual(j('"\u200B\u2028"'), '\u200B\u2028') + assert.throws(() => j('"\\v"'), /unexpected/) + assert.throws(() => j('"test'), /unterminated_string/) + assert.throws(() => j('"test\n"'), /unprintable/) + assert.throws(() => j('"\t"'), /unprintable/) + assert.throws(() => j('"\t "'), /unprintable/) + assert.throws(() => j('"\0 "'), /unprintable/) }) test('numbers', () => { - expect(j('0')).toEqual(0) - expect(j('0.1')).toEqual(0.1) - expect(j('-0.1')).toEqual(-0.1) - expect(j('-1')).toEqual(-1) - expect(j('1')).toEqual(1) - expect(j('123456789')).toEqual(123456789) - expect(j('10')).toEqual(10) - expect(j('90')).toEqual(90) - expect(j('90E+123')).toEqual(90E+123) - expect(j('90e+123')).toEqual(90e+123) - expect(j('90e-123')).toEqual(90e-123) - expect(j('90E-123')).toEqual(90E-123) - expect(j('90E123')).toEqual(90E123) - expect(j('90e123')).toEqual(90e123) - expect(j('01')).toEqual(1) - expect(j('-01')).toEqual(-1) - expect(() => j('-')).toThrow('unexpected') - expect(() => j('.0')).toThrow('unexpected') + assert.deepEqual(j('0'), 0) + assert.deepEqual(j('0.1'), 0.1) + assert.deepEqual(j('-0.1'), -0.1) + assert.deepEqual(j('-1'), -1) + assert.deepEqual(j('1'), 1) + assert.deepEqual(j('123456789'), 123456789) + assert.deepEqual(j('10'), 10) + assert.deepEqual(j('90'), 90) + assert.deepEqual(j('90E+123'), 90E+123) + assert.deepEqual(j('90e+123'), 90e+123) + assert.deepEqual(j('90e-123'), 90e-123) + assert.deepEqual(j('90E-123'), 90E-123) + assert.deepEqual(j('90E123'), 90E123) + assert.deepEqual(j('90e123'), 90e123) + assert.deepEqual(j('01'), 1) + assert.deepEqual(j('-01'), -1) + assert.throws(() => j('-'), /unexpected/) + assert.throws(() => j('.0'), /unexpected/) }) test('keywords', () => { - expect(j('true')).toEqual(true) - expect(j('false')).toEqual(false) - expect(j('null')).toEqual(null) + assert.deepEqual(j('true'), true) + assert.deepEqual(j('false'), false) + assert.deepEqual(j('null'), null) - expect(() => j('nulllll')).toThrow('unexpected') - expect(() => j('True')).toThrow('unexpected') - expect(() => j('foo-bar')).toThrow('unexpected') - expect(() => j('foo bar')).toThrow('unexpected') + assert.throws(() => j('nulllll'), /unexpected/) + assert.throws(() => j('True'), /unexpected/) + assert.throws(() => j('foo-bar'), /unexpected/) + assert.throws(() => j('foo bar'), /unexpected/) - expect(j('false//hello')).toEqual(false) + assert.deepEqual(j('false//hello'), false) }) test('trivia', () => { - expect(j(' ')).toEqual(undefined) - expect(j(' \t ')).toEqual(undefined) - expect(j(' \t \n \t ')).toEqual(undefined) - expect(j('\r\n')).toEqual(undefined) - expect(j('\r')).toEqual(undefined) - expect(j('\n')).toEqual(undefined) - expect(j('\n\r')).toEqual(undefined) - expect(j('\n \n')).toEqual(undefined) + assert.deepEqual(j(' '), undefined) + assert.deepEqual(j(' \t '), undefined) + assert.deepEqual(j(' \t \n \t '), undefined) + assert.deepEqual(j('\r\n'), undefined) + assert.deepEqual(j('\r'), undefined) + assert.deepEqual(j('\n'), undefined) + assert.deepEqual(j('\n\r'), undefined) + assert.deepEqual(j('\n \n'), undefined) }) test('literals', () => { - expect(j('true')).toEqual(true) - expect(j('false')).toEqual(false) - expect(j('null')).toEqual(null) - expect(j('"foo"')).toEqual('foo') - expect(j('"\\"-\\\\-\\/-\\b-\\f-\\n-\\r-\\t"')).toEqual('"-\\-/-\b-\f-\n-\r-\t') - expect(j('"\\u00DC"')).toEqual('Ü') - expect(j('9')).toEqual(9) - expect(j('-9')).toEqual(-9) - expect(j('0.129')).toEqual(0.129) - expect(j('23e3')).toEqual(23e3) - expect(j('1.2E+3')).toEqual(1.2E+3) - expect(j('1.2E-3')).toEqual(1.2E-3) - expect(j('1.2E-3 // comment')).toEqual(1.2E-3) + assert.deepEqual(j('true'), true) + assert.deepEqual(j('false'), false) + assert.deepEqual(j('null'), null) + assert.deepEqual(j('"foo"'), 'foo') + assert.deepEqual(j('"\\"-\\\\-\\/-\\b-\\f-\\n-\\r-\\t"'), '"-\\-/-\b-\f-\n-\r-\t') + assert.deepEqual(j('"\\u00DC"'), '\u00DC') + assert.deepEqual(j('9'), 9) + assert.deepEqual(j('-9'), -9) + assert.deepEqual(j('0.129'), 0.129) + assert.deepEqual(j('23e3'), 23e3) + assert.deepEqual(j('1.2E+3'), 1.2E+3) + assert.deepEqual(j('1.2E-3'), 1.2E-3) + assert.deepEqual(j('1.2E-3 // comment'), 1.2E-3) }) test('objects', () => { - expect(j('{}')).toEqual({}) - expect(j('{ "foo": true }')).toEqual({ foo: true }) - expect(j('{ "bar": 8, "xoo": "foo" }')).toEqual({ bar: 8, xoo: 'foo' }) - expect(j('{ "hello": [], "world": {} }')).toEqual({ hello: [], world: {} }) - expect(j('{ "a": false, "b": true, "c": [ 7.4 ] }')).toEqual({ a: false, b: true, c: [7.4] }) - expect(j('{ "lineComment": "//", "blockComment": ["/*", "*/"], "brackets": [ ["{", "}"], ["[", "]"], ["(", ")"] ] }')) - .toEqual({ lineComment: '//', blockComment: ['/*', '*/'], brackets: [['{', '}'], ['[', ']'], ['(', ')']] }) - expect(j('{ "hello": [], "world": {} }')).toEqual({ hello: [], world: {} }) - expect(j('{ "hello": { "again": { "inside": 5 }, "world": 1 }}')).toEqual({ hello: { again: { inside: 5 }, world: 1 } }) - expect(j('{ "foo": /*hello*/true }')).toEqual({ foo: true }) - expect(j('{ "": true }')).toEqual({ '': true }) + assert.deepEqual(j('{}'), {}) + assert.deepEqual(j('{ "foo": true }'), { foo: true }) + assert.deepEqual(j('{ "bar": 8, "xoo": "foo" }'), { bar: 8, xoo: 'foo' }) + assert.deepEqual(j('{ "hello": [], "world": {} }'), { hello: [], world: {} }) + assert.deepEqual(j('{ "a": false, "b": true, "c": [ 7.4 ] }'), { a: false, b: true, c: [7.4] }) + assert.deepEqual(j('{ "lineComment": "//", "blockComment": ["/*", "*/"], "brackets": [ ["{", "}"], ["[", "]"], ["(", ")"] ] }'), + { lineComment: '//', blockComment: ['/*', '*/'], brackets: [['{', '}'], ['[', ']'], ['(', ')']] }) + assert.deepEqual(j('{ "hello": [], "world": {} }'), { hello: [], world: {} }) + assert.deepEqual(j('{ "hello": { "again": { "inside": 5 }, "world": 1 }}'), { hello: { again: { inside: 5 }, world: 1 } }) + assert.deepEqual(j('{ "foo": /*hello*/true }'), { foo: true }) + assert.deepEqual(j('{ "": true }'), { '': true }) }) test('arrays', () => { - expect(j('[]')).toEqual([]) - expect(j('[ [], [ [] ]]')).toEqual([[], [[]]]) - expect(j('[ 1, 2, 3 ]')).toEqual([1, 2, 3]) - expect(j('[ { "a": null } ]')).toEqual([{ a: null }]) + assert.deepEqual(j('[]'), []) + assert.deepEqual(j('[ [], [ [] ]]'), [[], [[]]]) + assert.deepEqual(j('[ 1, 2, 3 ]'), [1, 2, 3]) + assert.deepEqual(j('[ { "a": null } ]'), [{ a: null }]) }) test('objects with errors', () => { - expect(() => j('{,}')).toThrow() - expect(() => j('{ "foo": true, }')).toThrow() - expect(() => j('{ "bar": 8 "xoo": "foo" }')).toThrow() - expect(() => j('{ ,"bar": 8 }')).toThrow() - expect(() => j('{ ,"bar": 8, "foo" }')).toThrow() - expect(() => j('{ "bar": 8, "foo": }')).toThrow() - expect(() => j('{ 8, "foo": 9 }')).toThrow() + assert.throws(() => j('{,}')) + assert.throws(() => j('{ "foo": true, }')) + assert.throws(() => j('{ "bar": 8 "xoo": "foo" }')) + assert.throws(() => j('{ ,"bar": 8 }')) + assert.throws(() => j('{ ,"bar": 8, "foo" }')) + assert.throws(() => j('{ "bar": 8, "foo": }')) + assert.throws(() => j('{ 8, "foo": 9 }')) }) - test('parse: array with errors', () => { - expect(() => j('[,]')).toThrow() - expect(() => j('[ 1 2, 3 ]')).toThrow() - expect(() => j('[ ,1, 2, 3 ]')).toThrow() - expect(() => j('[ ,1, 2, 3, ]')).toThrow() + test('array with errors', () => { + assert.throws(() => j('[,]')) + assert.throws(() => j('[ 1 2, 3 ]')) + assert.throws(() => j('[ ,1, 2, 3 ]')) + assert.throws(() => j('[ ,1, 2, 3, ]')) }) test('errors', () => { - expect(() => j('1,1')).toThrow() - expect(() => j('')).toThrow() + assert.throws(() => j('1,1')) + assert.throws(() => j('')) }) test('disallow comments', () => { const nc = Jsonic.make().use(Jsonc, { disallowComments: true }) - expect(nc('[ 1, 2, null, "foo" ]')).toEqual([1, 2, null, 'foo']) - expect(nc('{ "hello": [], "world": {} }')).toEqual({ hello: [], world: {} }) + assert.deepEqual(nc('[ 1, 2, null, "foo" ]'), [1, 2, null, 'foo']) + assert.deepEqual(nc('{ "hello": [], "world": {} }'), { hello: [], world: {} }) - expect(() => nc('{ "foo": /*comment*/ true }')).toThrow() + assert.throws(() => nc('{ "foo": /*comment*/ true }')) }) test('trailing comma', () => { const jc = Jsonic.make().use(Jsonc, { allowTrailingComma: true }) - expect(jc('{ "hello": [], }')).toEqual({ hello: [] }) - expect(jc('{ "hello": [] }')).toEqual({ hello: [] }) - expect(jc('{ "hello": [], "world": {}, }')).toEqual({ hello: [], world: {} }) - expect(jc('{ "hello": [], "world": {} }')).toEqual({ hello: [], world: {} }) - expect(jc('[ 1, 2, ]')).toEqual([1, 2]) - expect(jc('[ 1, 2 ]')).toEqual([1, 2]) + assert.deepEqual(jc('{ "hello": [], }'), { hello: [] }) + assert.deepEqual(jc('{ "hello": [] }'), { hello: [] }) + assert.deepEqual(jc('{ "hello": [], "world": {}, }'), { hello: [], world: {} }) + assert.deepEqual(jc('{ "hello": [], "world": {} }'), { hello: [], world: {} }) + assert.deepEqual(jc('[ 1, 2, ]'), [1, 2]) + assert.deepEqual(jc('[ 1, 2 ]'), [1, 2]) - expect(() => j('{ "hello": [], }')).toThrow() - expect(() => j('{ "hello": [], "world": {}, }')).toThrow() - expect(() => j('[ 1, 2, ]')).toThrow() + assert.throws(() => j('{ "hello": [], }')) + assert.throws(() => j('{ "hello": [], "world": {}, }')) + assert.throws(() => j('[ 1, 2, ]')) }) test('misc', () => { - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }) - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }) - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }) - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }) - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }) - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }) - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }) - expect(j('{ "foo": {"bar": 1, "car": 2 } }')).toEqual({ "foo": { "bar": 1, "car": 2 } }) - expect(j('{ "foo": {"bar": 1, "car": 3 } }')).toEqual({ "foo": { "bar": 1, "car": 3 } }) - expect(j('{ "foo": {"bar": 1, "car": 4 } }')).toEqual({ "foo": { "bar": 1, "car": 4 } }) - expect(j('{ "foo": {"bar": 1, "car": 5 } }')).toEqual({ "foo": { "bar": 1, "car": 5 } }) - expect(j('{ "foo": {"bar": 1, "car": 6 } }')).toEqual({ "foo": { "bar": 1, "car": 6 } }) - expect(j('{ "foo": {"bar": 1, "car": 7 } }')).toEqual({ "foo": { "bar": 1, "car": 7 } }) - expect(j('{ "foo": {"bar": 1, "car": 8 }, "goo": {} }')).toEqual({ "foo": { "bar": 1, "car": 8 }, "goo": {} }) - expect(j('{ "foo": {"bar": 1, "car": 9 }, "goo": {} }')).toEqual({ "foo": { "bar": 1, "car": 9 }, "goo": {} }) - expect(() => j('{ "dep": {"bar": 1, "car": ')).toThrow() - expect(() => j('{ "dep": {"bar": 1,, "car": ')).toThrow() - expect(() => j('{ "dep": {"bar": "na", "dar": "ma", "car": } }')).toThrow() - - expect(j('["foo", null ]')).toEqual(["foo", null]) - expect(j('["foo", null ]')).toEqual(["foo", null]) - expect(j('["foo", null ]')).toEqual(["foo", null]) - expect(j('["foo", null ]')).toEqual(["foo", null]) - expect(j('["foo", null ]')).toEqual(["foo", null]) - expect(() => j('["foo", null, ]')).toThrow() - - // TODO - expect(() => j('["foo", null,, ]')).toThrow() - expect(() => j('[["foo", null,, ],')).toThrow() - - expect(j('true')).toEqual(true) - expect(j('false')).toEqual(false) - expect(j('null')).toEqual(null) - expect(j('23')).toEqual(23) - expect(j('-1.93e-19')).toEqual(-1.93e-19) - expect(j('"hello"')).toEqual("hello") - - expect(j('[]')).toEqual([]) - expect(j('[ 1 ]')).toEqual([1]) - expect(j('[ 1, "x"]')).toEqual([1, "x"]) - expect(j('[[]]')).toEqual([[]]) - expect(j('{ }')).toEqual({}) - expect(j('{ "val": 1 }')).toEqual({ "val": 1 }) - expect(j('{"id": "$", "v": [ null, null] }')) - .toEqual({ "id": "$", "v": [null, null] }) - - expect(() => j('{ "id": { "foo": { } } , }')).toThrow() - - expect(j('{ }')).toEqual({}) //, [{ id: 'onObjectBegin', text: '{', startLine: 0, startCharacter: 0 }, { id: 'onObjectEnd', text: '}', startLine: 0, startCharacter: 2 }]); - expect(j('{ "foo": "bar" }')).toEqual({ "foo": "bar" }) - expect(j('{ "foo": { "goo": 3 } }')).toEqual({ "foo": { "goo": 3 } }) - expect(j('[]')).toEqual([]) - expect(j('[ true, null, [] ]')).toEqual([true, null, []]) - expect(j('[\r\n0,\r\n1,\r\n2\r\n]')).toEqual([0, 1, 2]) - expect(j('/* g */ { "foo": //f\n"bar" }')).toEqual({ foo: 'bar' }) - expect(j('/* g\r\n */ { "foo": //f\n"bar" }')).toEqual({ foo: 'bar' }) - expect(j('/* g\n */ { "foo": //f\n"bar"\n}')).toEqual({ foo: 'bar' }) - expect(() => j('{"prop1":"foo","prop2":"foo2","prop3":{"prp1":{""}}}')).toThrow() - expect(j('{ "key1": { "key11": [ "val111", "val112" ] }, "key2": [ { "key21": false, "key22": 221 }, null, [{}] ] }')) - .toEqual({ "key1": { "key11": ["val111", "val112"] }, "key2": [{ "key21": false, "key22": 221 }, null, [{}]] }) + assert.deepEqual(j('{ "foo": "bar" }'), { "foo": "bar" }) + assert.deepEqual(j('{ "foo": {"bar": 1, "car": 2 } }'), { "foo": { "bar": 1, "car": 2 } }) + assert.deepEqual(j('{ "foo": {"bar": 1, "car": 8 }, "goo": {} }'), { "foo": { "bar": 1, "car": 8 }, "goo": {} }) + assert.throws(() => j('{ "dep": {"bar": 1, "car": ')) + assert.throws(() => j('{ "dep": {"bar": 1,, "car": ')) + assert.throws(() => j('{ "dep": {"bar": "na", "dar": "ma", "car": } }')) + + assert.deepEqual(j('["foo", null ]'), ["foo", null]) + assert.throws(() => j('["foo", null, ]')) + assert.throws(() => j('["foo", null,, ]')) + assert.throws(() => j('[["foo", null,, ],')) + + assert.deepEqual(j('true'), true) + assert.deepEqual(j('false'), false) + assert.deepEqual(j('null'), null) + assert.deepEqual(j('23'), 23) + assert.deepEqual(j('-1.93e-19'), -1.93e-19) + assert.deepEqual(j('"hello"'), "hello") + + assert.deepEqual(j('[]'), []) + assert.deepEqual(j('[ 1 ]'), [1]) + assert.deepEqual(j('[ 1, "x"]'), [1, "x"]) + assert.deepEqual(j('[[]]'), [[]]) + assert.deepEqual(j('{ }'), {}) + assert.deepEqual(j('{ "val": 1 }'), { "val": 1 }) + assert.deepEqual(j('{"id": "$", "v": [ null, null] }'), + { "id": "$", "v": [null, null] }) + + assert.throws(() => j('{ "id": { "foo": { } } , }')) + + assert.deepEqual(j('{ }'), {}) + assert.deepEqual(j('{ "foo": "bar" }'), { "foo": "bar" }) + assert.deepEqual(j('{ "foo": { "goo": 3 } }'), { "foo": { "goo": 3 } }) + assert.deepEqual(j('[]'), []) + assert.deepEqual(j('[ true, null, [] ]'), [true, null, []]) + assert.deepEqual(j('[\r\n0,\r\n1,\r\n2\r\n]'), [0, 1, 2]) + assert.deepEqual(j('/* g */ { "foo": //f\n"bar" }'), { foo: 'bar' }) + assert.deepEqual(j('/* g\r\n */ { "foo": //f\n"bar" }'), { foo: 'bar' }) + assert.deepEqual(j('/* g\n */ { "foo": //f\n"bar"\n}'), { foo: 'bar' }) + assert.throws(() => j('{"prop1":"foo","prop2":"foo2","prop3":{"prp1":{""}}}' )) + assert.deepEqual(j('{ "key1": { "key11": [ "val111", "val112" ] }, "key2": [ { "key21": false, "key22": 221 }, null, [{}] ] }'), + { "key1": { "key11": ["val111", "val112"] }, "key2": [{ "key21": false, "key22": 221 }, null, [{}]] }) }) }) - - diff --git a/test/quick.js b/test/quick.js deleted file mode 100644 index 29110e0..0000000 --- a/test/quick.js +++ /dev/null @@ -1,18 +0,0 @@ -const { Jsonic } = require('@jsonic/jsonic-next') -const { Debug } = require('@jsonic/jsonic-next/debug') - -console.log(Debug) - -const { Jsonc } = require('..') - -const jsonc = Jsonic.make() - .use(Debug, { - trace: true, - }) - .use(Jsonc, {}) - -// console.log(jsonc.internal().config) - -// console.dir(jsonc(`// comment`),{depth:null}) -// console.dir(jsonc('"\\v"'),{depth:null}) -console.dir(jsonc('.0'), { depth: null })