@o3co/ts.hocon 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- package/.github/pull_request_template.md +17 -0
- package/.github/workflows/lint.yml +33 -0
- package/.github/workflows/release.yml +41 -0
- package/.github/workflows/test.yml +53 -0
- package/CHANGELOG.md +22 -0
- package/CONTRIBUTING.md +62 -0
- package/LICENSE +202 -0
- package/README.ja.md +195 -0
- package/README.md +195 -0
- package/SECURITY.md +20 -0
- package/docs/superpowers/plans/2026-03-20-ts-hocon.md +2412 -0
- package/docs/superpowers/specs/2026-03-20-ts-hocon-design.md +368 -0
- package/package.json +44 -0
- package/src/config.ts +108 -0
- package/src/errors.ts +34 -0
- package/src/index.ts +5 -0
- package/src/internal/lexer/lexer.ts +168 -0
- package/src/internal/lexer/token.ts +20 -0
- package/src/internal/parser/ast.ts +17 -0
- package/src/internal/parser/parser.ts +229 -0
- package/src/internal/resolver/resolver.ts +428 -0
- package/src/parse.ts +79 -0
- package/src/value.ts +5 -0
- package/src/zod.ts +12 -0
- package/tests/config.test.ts +100 -0
- package/tests/errors.test.ts +43 -0
- package/tests/lexer.test.ts +141 -0
- package/tests/lightbend/lightbend.test.ts +39 -0
- package/tests/lightbend/testdata/a_1.conf +1 -0
- package/tests/lightbend/testdata/b_2.conf +1 -0
- package/tests/lightbend/testdata/beanconfig/beanconfig01.conf +131 -0
- package/tests/lightbend/testdata/bom.conf +2 -0
- package/tests/lightbend/testdata/cycle.conf +1 -0
- package/tests/lightbend/testdata/env-variables.conf +16 -0
- package/tests/lightbend/testdata/equiv01/comments.conf +45 -0
- package/tests/lightbend/testdata/equiv01/equals.conf +41 -0
- package/tests/lightbend/testdata/equiv01/no-commas.conf +55 -0
- package/tests/lightbend/testdata/equiv01/no-root-braces.conf +40 -0
- package/tests/lightbend/testdata/equiv01/no-whitespace.json +1 -0
- package/tests/lightbend/testdata/equiv01/omit-colons.conf +44 -0
- package/tests/lightbend/testdata/equiv01/original.json +41 -0
- package/tests/lightbend/testdata/equiv01/path-keys.conf +29 -0
- package/tests/lightbend/testdata/equiv01/properties-style.conf +37 -0
- package/tests/lightbend/testdata/equiv01/substitutions.conf +41 -0
- package/tests/lightbend/testdata/equiv01/unquoted.conf +41 -0
- package/tests/lightbend/testdata/equiv02/original.json +17 -0
- package/tests/lightbend/testdata/equiv02/path-keys-weird-whitespace.conf +32 -0
- package/tests/lightbend/testdata/equiv02/path-keys.conf +9 -0
- package/tests/lightbend/testdata/equiv03/includes.conf +7 -0
- package/tests/lightbend/testdata/equiv03/letters/a.conf +6 -0
- package/tests/lightbend/testdata/equiv03/letters/b.json +3 -0
- package/tests/lightbend/testdata/equiv03/letters/c.conf +2 -0
- package/tests/lightbend/testdata/equiv03/letters/c.properties +1 -0
- package/tests/lightbend/testdata/equiv03/letters/numbers/1.conf +1 -0
- package/tests/lightbend/testdata/equiv03/letters/numbers/2.properties +1 -0
- package/tests/lightbend/testdata/equiv03/original.json +15 -0
- package/tests/lightbend/testdata/equiv03/root/foo.conf +1 -0
- package/tests/lightbend/testdata/equiv04/missing-substitutions.conf +4 -0
- package/tests/lightbend/testdata/equiv04/original.json +3 -0
- package/tests/lightbend/testdata/equiv05/original.json +16 -0
- package/tests/lightbend/testdata/equiv05/triple-quotes.conf +20 -0
- package/tests/lightbend/testdata/file-include.conf +5 -0
- package/tests/lightbend/testdata/include-from-list.conf +5 -0
- package/tests/lightbend/testdata/onclasspath.conf +2 -0
- package/tests/lightbend/testdata/subdir/bar-file.conf +1 -0
- package/tests/lightbend/testdata/subdir/bar.conf +1 -0
- package/tests/lightbend/testdata/subdir/baz.conf +1 -0
- package/tests/lightbend/testdata/subdir/foo.conf +5 -0
- package/tests/lightbend/testdata/test01.conf +96 -0
- package/tests/lightbend/testdata/test01.json +4 -0
- package/tests/lightbend/testdata/test01.properties +5 -0
- package/tests/lightbend/testdata/test02.conf +13 -0
- package/tests/lightbend/testdata/test03-included.conf +9 -0
- package/tests/lightbend/testdata/test03.conf +36 -0
- package/tests/lightbend/testdata/test04.conf +305 -0
- package/tests/lightbend/testdata/test05.conf +154 -0
- package/tests/lightbend/testdata/test06.conf +14 -0
- package/tests/lightbend/testdata/test07.conf +4 -0
- package/tests/lightbend/testdata/test08.conf +6 -0
- package/tests/lightbend/testdata/test09.conf +17 -0
- package/tests/lightbend/testdata/test10.conf +11 -0
- package/tests/lightbend/testdata/test11.conf +7 -0
- package/tests/lightbend/testdata/test12.conf +43 -0
- package/tests/lightbend/testdata/test13-application-override-substitutions.conf +1 -0
- package/tests/lightbend/testdata/test13-reference-bad-substitutions.conf +1 -0
- package/tests/lightbend/testdata/test13-reference-with-substitutions.conf +2 -0
- package/tests/lightbend/testdata/validate-invalid.conf +30 -0
- package/tests/lightbend/testdata/validate-reference.conf +32 -0
- package/tests/parse.test.ts +44 -0
- package/tests/parser.test.ts +154 -0
- package/tests/resolver.test.ts +159 -0
- package/tests/zod.test.ts +57 -0
- package/tsconfig.json +16 -0
- package/tsup.config.ts +12 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { tokenize } from '../src/internal/lexer/lexer.js'
|
|
3
|
+
import { ParseError } from '../src/errors.js'
|
|
4
|
+
|
|
5
|
+
describe('tokenize', () => {
|
|
6
|
+
it('tokenizes empty string', () => {
|
|
7
|
+
const tokens = tokenize('')
|
|
8
|
+
expect(tokens).toHaveLength(1)
|
|
9
|
+
expect(tokens[0].kind).toBe('eof')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('tokenizes braces and brackets', () => {
|
|
13
|
+
const tokens = tokenize('{}[]')
|
|
14
|
+
expect(tokens.map(t => t.kind)).toEqual(['lbrace', 'rbrace', 'lbracket', 'rbracket', 'eof'])
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('tokenizes = and +=', () => {
|
|
18
|
+
const tokens = tokenize('=+=')
|
|
19
|
+
expect(tokens[0].kind).toBe('equals')
|
|
20
|
+
expect(tokens[1].kind).toBe('plus_equals')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('tokenizes : and ,', () => {
|
|
24
|
+
const tokens = tokenize(':,')
|
|
25
|
+
expect(tokens.map(t => t.kind)).toEqual(['colon', 'comma', 'eof'])
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('skips // comments, keeps newline', () => {
|
|
29
|
+
const tokens = tokenize('// comment\nfoo')
|
|
30
|
+
expect(tokens[0].kind).toBe('newline')
|
|
31
|
+
expect(tokens[1].kind).toBe('unquoted')
|
|
32
|
+
expect(tokens[1].value).toBe('foo')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('skips # comments', () => {
|
|
36
|
+
const tokens = tokenize('# comment\nfoo')
|
|
37
|
+
expect(tokens[0].kind).toBe('newline')
|
|
38
|
+
expect(tokens[1].value).toBe('foo')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('tokenizes quoted strings', () => {
|
|
42
|
+
const [t] = tokenize('"hello world"')
|
|
43
|
+
expect(t.kind).toBe('string')
|
|
44
|
+
expect(t.value).toBe('hello world')
|
|
45
|
+
expect(t.isQuoted).toBe(true)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('handles escape sequences', () => {
|
|
49
|
+
const [t] = tokenize('"a\\nb\\tc"')
|
|
50
|
+
expect(t.value).toBe('a\nb\tc')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('handles \\u unicode escapes', () => {
|
|
54
|
+
const [t] = tokenize('"\\u0041"')
|
|
55
|
+
expect(t.value).toBe('A')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('tokenizes triple-quoted strings', () => {
|
|
59
|
+
const [t] = tokenize('"""hello\nworld"""')
|
|
60
|
+
expect(t.kind).toBe('triple_string')
|
|
61
|
+
expect(t.value).toBe('hello\nworld')
|
|
62
|
+
expect(t.isQuoted).toBe(true)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('strips leading newline from triple-quoted strings', () => {
|
|
66
|
+
const [t] = tokenize('"""\nhello"""')
|
|
67
|
+
expect(t.value).toBe('hello')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('tokenizes unquoted strings', () => {
|
|
71
|
+
const [t] = tokenize('localhost')
|
|
72
|
+
expect(t.kind).toBe('unquoted')
|
|
73
|
+
expect(t.value).toBe('localhost')
|
|
74
|
+
expect(t.isQuoted).toBe(false)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('tokenizes numbers as unquoted', () => {
|
|
78
|
+
const [t] = tokenize('8080')
|
|
79
|
+
expect(t.kind).toBe('unquoted')
|
|
80
|
+
expect(t.value).toBe('8080')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('tokenizes substitutions', () => {
|
|
84
|
+
const [t] = tokenize('${server.host}')
|
|
85
|
+
expect(t.kind).toBe('subst')
|
|
86
|
+
expect(t.value).toBe('server.host')
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('tokenizes optional substitutions', () => {
|
|
90
|
+
const [t] = tokenize('${?foo}')
|
|
91
|
+
expect(t.kind).toBe('opt_subst')
|
|
92
|
+
expect(t.value).toBe('foo')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('tokenizes newlines', () => {
|
|
96
|
+
const tokens = tokenize('a\nb')
|
|
97
|
+
expect(tokens[1].kind).toBe('newline')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('deduplicates consecutive newlines', () => {
|
|
101
|
+
const tokens = tokenize('a\n\n\nb')
|
|
102
|
+
const newlines = tokens.filter(t => t.kind === 'newline')
|
|
103
|
+
expect(newlines).toHaveLength(1)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('tracks line and col', () => {
|
|
107
|
+
const tokens = tokenize('a\nb')
|
|
108
|
+
expect(tokens[0].line).toBe(1)
|
|
109
|
+
expect(tokens[0].col).toBe(1)
|
|
110
|
+
expect(tokens[2].line).toBe(2)
|
|
111
|
+
expect(tokens[2].col).toBe(1)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('sets precedingSpace on token after whitespace', () => {
|
|
115
|
+
const tokens = tokenize('a b')
|
|
116
|
+
expect(tokens[1].precedingSpace).toBe(true)
|
|
117
|
+
expect(tokens[0].precedingSpace).toBe(false)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('strips UTF-8 BOM', () => {
|
|
121
|
+
const tokens = tokenize('\uFEFFfoo')
|
|
122
|
+
expect(tokens[0].value).toBe('foo')
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('stops unquoted scan at $ for concat', () => {
|
|
126
|
+
const tokens = tokenize('foo${bar}')
|
|
127
|
+
expect(tokens[0].kind).toBe('unquoted')
|
|
128
|
+
expect(tokens[0].value).toBe('foo')
|
|
129
|
+
expect(tokens[1].kind).toBe('subst')
|
|
130
|
+
expect(tokens[1].value).toBe('bar')
|
|
131
|
+
expect(tokens[1].precedingSpace).toBe(false)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('throws ParseError on unterminated string', () => {
|
|
135
|
+
expect(() => tokenize('"unterminated')).toThrow(ParseError)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('throws ParseError on unterminated substitution', () => {
|
|
139
|
+
expect(() => tokenize('${foo')).toThrow(ParseError)
|
|
140
|
+
})
|
|
141
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// tests/lightbend/lightbend.test.ts
|
|
2
|
+
//
|
|
3
|
+
// Lightbend HOCON spec compliance tests, mirroring go.hocon's lightbend_test.go.
|
|
4
|
+
// Tests equiv01-05 directories (each .conf compared against original.json).
|
|
5
|
+
import { describe, it, expect } from 'vitest'
|
|
6
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
7
|
+
import { join } from 'node:path'
|
|
8
|
+
import { parse } from '../../src/parse.js'
|
|
9
|
+
|
|
10
|
+
const dataDir = new URL('./testdata', import.meta.url).pathname
|
|
11
|
+
|
|
12
|
+
// Known skip reasons for specific test files
|
|
13
|
+
const equivSkip = new Set([
|
|
14
|
+
'equiv03/includes.conf', // requires .properties file support
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
describe('Lightbend HOCON equiv tests', () => {
|
|
18
|
+
for (let i = 1; i <= 5; i++) {
|
|
19
|
+
const dir = join(dataDir, `equiv${String(i).padStart(2, '0')}`)
|
|
20
|
+
const jsonPath = join(dir, 'original.json')
|
|
21
|
+
if (!existsSync(jsonPath)) continue
|
|
22
|
+
|
|
23
|
+
const expected = JSON.parse(readFileSync(jsonPath, 'utf-8'))
|
|
24
|
+
const entries = readdirSync(dir)
|
|
25
|
+
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
if (!entry.endsWith('.conf')) continue
|
|
28
|
+
const rel = `equiv${String(i).padStart(2, '0')}/${entry}`
|
|
29
|
+
if (equivSkip.has(rel)) continue
|
|
30
|
+
|
|
31
|
+
it(rel, () => {
|
|
32
|
+
const confPath = join(dir, entry)
|
|
33
|
+
const confContent = readFileSync(confPath, 'utf-8')
|
|
34
|
+
const config = parse(confContent, { baseDir: dir })
|
|
35
|
+
expect(config.toObject()).toEqual(expected)
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
a=1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
b=2
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
{
|
|
2
|
+
"booleans" : {
|
|
3
|
+
"trueVal" : true,
|
|
4
|
+
"trueValAgain" : ${booleans.trueVal},
|
|
5
|
+
"falseVal" : false,
|
|
6
|
+
"falseValAgain" : ${booleans.falseVal},
|
|
7
|
+
"on" : "on",
|
|
8
|
+
"off" : "off"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
"numbers" : {
|
|
12
|
+
"byteVal" : "1",
|
|
13
|
+
"byteObj" : ${numbers.byteVal},
|
|
14
|
+
"shortVal" : "2",
|
|
15
|
+
"shortObj" : ${numbers.shortVal},
|
|
16
|
+
"intVal" : "3",
|
|
17
|
+
"intObj" : ${numbers.intVal},
|
|
18
|
+
"longVal" : "4",
|
|
19
|
+
"longObj" : ${numbers.longVal},
|
|
20
|
+
"doubleVal" : "1.0",
|
|
21
|
+
"doubleObj" : ${numbers.doubleVal}
|
|
22
|
+
}
|
|
23
|
+
//
|
|
24
|
+
"strings" : {
|
|
25
|
+
"abcd" : "abcd",
|
|
26
|
+
"abcd-again" : ${strings.a}${strings.b}${strings.c}${strings.d},
|
|
27
|
+
"a" : "a",
|
|
28
|
+
"b" : "b",
|
|
29
|
+
"c" : "c",
|
|
30
|
+
"d" : "d",
|
|
31
|
+
"concatenated" : null bar 42 baz true 3.14 hi,
|
|
32
|
+
"double" : "3.14",
|
|
33
|
+
"number" : "57",
|
|
34
|
+
"null" : "null",
|
|
35
|
+
"true" : "true",
|
|
36
|
+
"yes" : "yes",
|
|
37
|
+
"false" : "false",
|
|
38
|
+
"no" : "no"
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
"arrays" : {
|
|
42
|
+
"empty" : [],
|
|
43
|
+
"ofInt" : [1, 2, 3],
|
|
44
|
+
"ofString" : [ ${strings.a}, ${strings.b}, ${strings.c} ],
|
|
45
|
+
"of-double" : [3.14, 4.14, 5.14],
|
|
46
|
+
"of-long" : { "1" : 32, "2" : 42, "3" : 52 }, // object-to-list conversion
|
|
47
|
+
"ofNull" : [null, null, null],
|
|
48
|
+
"ofBoolean" : [true, false],
|
|
49
|
+
"ofArray" : [${arrays.ofString}, ${arrays.ofString}, ${arrays.ofString}],
|
|
50
|
+
"ofObject" : [${numbers}, ${booleans}, ${strings}],
|
|
51
|
+
"ofConfig" : [${numbers}, ${booleans}, ${strings}],
|
|
52
|
+
"ofConfigObject" : [${numbers}, ${booleans}, ${strings}],
|
|
53
|
+
"ofConfigValue" : [1, 2, "a"],
|
|
54
|
+
"ofDuration" : [1, 2h, 3 days],
|
|
55
|
+
"ofMemorySize" : [1024, 1M, 1G],
|
|
56
|
+
"ofStringBean" : [
|
|
57
|
+
{
|
|
58
|
+
abcd : "testAbcdOne"
|
|
59
|
+
yes : "testYesOne"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
abcd : "testAbcdTwo"
|
|
63
|
+
yes : "testYesTwo"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
"bytes" : {
|
|
68
|
+
"kilobyte" : "1kB",
|
|
69
|
+
"kibibyte" : "1K",
|
|
70
|
+
"thousandBytes" : "1000B"
|
|
71
|
+
},
|
|
72
|
+
"durations" : {
|
|
73
|
+
"second" : 1s,
|
|
74
|
+
"secondAsNumber" : 1000,
|
|
75
|
+
"halfSecond" : 0.5s
|
|
76
|
+
},
|
|
77
|
+
"validation" : {
|
|
78
|
+
"shouldBeInt" : true,
|
|
79
|
+
"should-be-boolean" : 42,
|
|
80
|
+
"should-be-list" : "hello"
|
|
81
|
+
},
|
|
82
|
+
"preferCamelNames" : {
|
|
83
|
+
"foo-bar" : "no",
|
|
84
|
+
"fooBar" : "yes",
|
|
85
|
+
"baz-bar" : "no",
|
|
86
|
+
"bazBar" : "yes"
|
|
87
|
+
},
|
|
88
|
+
"values" : {
|
|
89
|
+
"obj" : 42,
|
|
90
|
+
"config" : ${strings},
|
|
91
|
+
"configObj" : ${numbers},
|
|
92
|
+
"configValue" : "hello world",
|
|
93
|
+
"list" : [1,2,3],
|
|
94
|
+
"unwrappedMap" : ${validation}
|
|
95
|
+
},
|
|
96
|
+
"enums" : {
|
|
97
|
+
"problem" : "P1",
|
|
98
|
+
"solutions" : ["S1", "S3"]
|
|
99
|
+
},
|
|
100
|
+
"objects" : {
|
|
101
|
+
"valueObject": {
|
|
102
|
+
"mandatoryValue": "notNull"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"sets" : {
|
|
106
|
+
"empty" : [],
|
|
107
|
+
"ofInt" : [1, 2, 3, 2, 3],
|
|
108
|
+
"ofString" : [ ${strings.a}, ${strings.b}, ${strings.c} ],
|
|
109
|
+
"of-double" : [3.14, 4.14, 4.14, 5.14],
|
|
110
|
+
"of-long" : { "1" : 32, "2" : 42, "3" : 52 }, // object-to-list conversion
|
|
111
|
+
"ofNull" : [null, null, null],
|
|
112
|
+
"ofBoolean" : [true, false, false],
|
|
113
|
+
"ofArray" : [${arrays.ofString}, ${arrays.ofString}, ${arrays.ofString}],
|
|
114
|
+
"ofObject" : [${numbers}, ${booleans}, ${strings}],
|
|
115
|
+
"ofConfig" : [${numbers}, ${booleans}, ${strings}],
|
|
116
|
+
"ofConfigObject" : [${numbers}, ${booleans}, ${strings}],
|
|
117
|
+
"ofConfigValue" : [1, 2, "a"],
|
|
118
|
+
"ofDuration" : [1, 2h, 3 days],
|
|
119
|
+
"ofMemorySize" : [1024, 1M, 1G],
|
|
120
|
+
"ofStringBean" : [
|
|
121
|
+
{
|
|
122
|
+
abcd : "testAbcdOne"
|
|
123
|
+
yes : "testYesOne"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
abcd : "testAbcdTwo"
|
|
127
|
+
yes : "testYesTwo"
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include "cycle.conf"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
secret = a
|
|
2
|
+
secret = ${?SECRET_A}
|
|
3
|
+
secrets = ["b", "c"]
|
|
4
|
+
secrets = [${?SECRET_B}, ${?SECRET_C}]
|
|
5
|
+
|
|
6
|
+
myList = ${MY_LIST[]}
|
|
7
|
+
myOptionalList = ${?MY_LIST[]}
|
|
8
|
+
myOptionalUndefinedList = ${?UNDEFINED_LIST[]}
|
|
9
|
+
numList = ${NUM_LIST[]}
|
|
10
|
+
|
|
11
|
+
# list concatenation with env var list expansion
|
|
12
|
+
prependedList = ["x", "y"] ${?MY_LIST[]}
|
|
13
|
+
appendedList = ${?MY_LIST[]} ["x", "y"]
|
|
14
|
+
selfAppendedList = ["x"]
|
|
15
|
+
selfAppendedList = ${?selfAppendedList} ${?MY_LIST[]}
|
|
16
|
+
appendedUndefined = ["x", "y"] ${?UNDEFINED_LIST[]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{ // These are some integers
|
|
2
|
+
"ints" : { # we can comment them with # too
|
|
3
|
+
"fortyTwo" : 42, ## double-# !
|
|
4
|
+
"fortyTwoAgain" : 42 # { }
|
|
5
|
+
},
|
|
6
|
+
######## COMMENT COMMENT COMMENT
|
|
7
|
+
/// I COMMENT YOU
|
|
8
|
+
"floats" : {
|
|
9
|
+
"fortyTwoPointOne" : 42.1,
|
|
10
|
+
"fortyTwoPointOneAgain" : 42.1
|
|
11
|
+
// I CAN HAS COMMENT
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
"strings" : {
|
|
15
|
+
"abcd" : // I got yr comment right here
|
|
16
|
+
"abcd",
|
|
17
|
+
"abcdAgain" : "abcd",
|
|
18
|
+
"a" : "a",
|
|
19
|
+
"b" : "b",
|
|
20
|
+
"c" : "c",
|
|
21
|
+
"d" : "d",
|
|
22
|
+
"concatenated" # more to say?
|
|
23
|
+
: "null bar 42 baz true 3.14 hi"
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
"arrays" : {
|
|
27
|
+
"empty" : [],
|
|
28
|
+
"1" : [ 1 ],
|
|
29
|
+
"12" : [1, 2],
|
|
30
|
+
"123" : [1, 2, 3],
|
|
31
|
+
"ofString" : [ "a", "b", "c" ]
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
"booleans" : {
|
|
35
|
+
"true" : true,
|
|
36
|
+
"trueAgain" : true,
|
|
37
|
+
"false" : false,
|
|
38
|
+
"falseAgain" : false
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
"nulls" : {
|
|
42
|
+
"null" : null,
|
|
43
|
+
"nullAgain" : null
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ints" = {
|
|
3
|
+
"fortyTwo" = 42,
|
|
4
|
+
"fortyTwoAgain" = 42
|
|
5
|
+
},
|
|
6
|
+
|
|
7
|
+
"floats" = {
|
|
8
|
+
"fortyTwoPointOne" = 42.1,
|
|
9
|
+
"fortyTwoPointOneAgain" = 42.1
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
"strings" = {
|
|
13
|
+
"abcd" = "abcd",
|
|
14
|
+
"abcdAgain" = "abcd",
|
|
15
|
+
"a" = "a",
|
|
16
|
+
"b" = "b",
|
|
17
|
+
"c" = "c",
|
|
18
|
+
"d" = "d",
|
|
19
|
+
"concatenated" = "null bar 42 baz true 3.14 hi"
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
"arrays" = {
|
|
23
|
+
"empty" = [],
|
|
24
|
+
"1" = [ 1 ],
|
|
25
|
+
"12" = [1, 2],
|
|
26
|
+
"123" = [1, 2, 3],
|
|
27
|
+
"ofString" = [ "a", "b", "c" ]
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
"booleans" = {
|
|
31
|
+
"true" = true,
|
|
32
|
+
"trueAgain" = true,
|
|
33
|
+
"false" = false,
|
|
34
|
+
"falseAgain" = false
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
"nulls" = {
|
|
38
|
+
"null" = null,
|
|
39
|
+
"nullAgain" = null
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ints" : {
|
|
3
|
+
"fortyTwo" : 42
|
|
4
|
+
"fortyTwoAgain" : 42
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
"floats" : {
|
|
8
|
+
"fortyTwoPointOne" : 42.1
|
|
9
|
+
"fortyTwoPointOneAgain" : 42.1
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
"strings" : {
|
|
13
|
+
"abcd" : "abcd"
|
|
14
|
+
"abcdAgain" : "abcd"
|
|
15
|
+
"a" : "a"
|
|
16
|
+
"b" : "b"
|
|
17
|
+
"c" : "c"
|
|
18
|
+
"d" : "d"
|
|
19
|
+
"concatenated" : "null bar 42 baz true 3.14 hi"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
"arrays" : {
|
|
23
|
+
"empty" : []
|
|
24
|
+
"1" : [ 1 ]
|
|
25
|
+
"12" : [1
|
|
26
|
+
2]
|
|
27
|
+
"123" : [1
|
|
28
|
+
2
|
|
29
|
+
3 ]
|
|
30
|
+
"ofString" : [
|
|
31
|
+
|
|
32
|
+
"a"
|
|
33
|
+
"b"
|
|
34
|
+
"c"
|
|
35
|
+
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
"booleans" : {
|
|
40
|
+
|
|
41
|
+
"true" : true
|
|
42
|
+
|
|
43
|
+
"trueAgain" : true
|
|
44
|
+
|
|
45
|
+
"false" : false
|
|
46
|
+
|
|
47
|
+
"falseAgain" : false
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
"nulls" : {
|
|
52
|
+
"null" : null
|
|
53
|
+
"nullAgain" : null
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"ints" : {
|
|
2
|
+
"fortyTwo" : 42,
|
|
3
|
+
"fortyTwoAgain" : 42
|
|
4
|
+
},
|
|
5
|
+
|
|
6
|
+
"floats" : {
|
|
7
|
+
"fortyTwoPointOne" : 42.1,
|
|
8
|
+
"fortyTwoPointOneAgain" : 42.1
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
"strings" : {
|
|
12
|
+
"abcd" : "abcd",
|
|
13
|
+
"abcdAgain" : "abcd",
|
|
14
|
+
"a" : "a",
|
|
15
|
+
"b" : "b",
|
|
16
|
+
"c" : "c",
|
|
17
|
+
"d" : "d",
|
|
18
|
+
"concatenated" : "null bar 42 baz true 3.14 hi"
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
"arrays" : {
|
|
22
|
+
"empty" : [],
|
|
23
|
+
"1" : [ 1 ],
|
|
24
|
+
"12" : [1, 2],
|
|
25
|
+
"123" : [1, 2, 3],
|
|
26
|
+
"ofString" : [ "a", "b", "c" ]
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
"booleans" : {
|
|
30
|
+
"true" : true,
|
|
31
|
+
"trueAgain" : true,
|
|
32
|
+
"false" : false,
|
|
33
|
+
"falseAgain" : false
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
"nulls" : {
|
|
37
|
+
"null" : null,
|
|
38
|
+
"nullAgain" : null
|
|
39
|
+
}
|
|
40
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"ints":{"fortyTwo":42,"fortyTwoAgain":42},"floats":{"fortyTwoPointOne":42.1,"fortyTwoPointOneAgain":42.1},"strings":{"abcd":"abcd","abcdAgain":"abcd","a":"a","b":"b","c":"c","d":"d","concatenated":"null bar 42 baz true 3.14 hi"},"arrays":{"empty":[],"1":[1],"12":[1,2],"123":[1,2,3],"ofString":["a","b","c"]},"booleans":{"true":true,"trueAgain":true,"false":false,"falseAgain":false},"nulls":{"null":null,"nullAgain":null}}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ints" {
|
|
3
|
+
"fortyTwo" : 42,
|
|
4
|
+
"fortyTwoAgain" : 42
|
|
5
|
+
},
|
|
6
|
+
|
|
7
|
+
"floats"
|
|
8
|
+
{
|
|
9
|
+
"fortyTwoPointOne" : 42.1,
|
|
10
|
+
"fortyTwoPointOneAgain" : 42.1
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
"strings"
|
|
14
|
+
|
|
15
|
+
{
|
|
16
|
+
"abcd" : "abcd",
|
|
17
|
+
"abcdAgain" : "abcd",
|
|
18
|
+
"a" : "a",
|
|
19
|
+
"b" : "b",
|
|
20
|
+
"c" : "c",
|
|
21
|
+
"d" : "d",
|
|
22
|
+
"concatenated" : "null bar 42 baz true 3.14 hi"
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
"arrays" {
|
|
26
|
+
"empty" : [],
|
|
27
|
+
"1" : [ 1 ],
|
|
28
|
+
"12" : [1, 2],
|
|
29
|
+
"123" : [1, 2, 3],
|
|
30
|
+
"ofString" : [ "a", "b", "c" ]
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
"booleans" {
|
|
34
|
+
"true" : true,
|
|
35
|
+
"trueAgain" : true,
|
|
36
|
+
"false" : false,
|
|
37
|
+
"falseAgain" : false
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
"nulls" {
|
|
41
|
+
"null" : null,
|
|
42
|
+
"nullAgain" : null
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ints" : {
|
|
3
|
+
"fortyTwo" : 42,
|
|
4
|
+
"fortyTwoAgain" : 42
|
|
5
|
+
},
|
|
6
|
+
|
|
7
|
+
"floats" : {
|
|
8
|
+
"fortyTwoPointOne" : 42.1,
|
|
9
|
+
"fortyTwoPointOneAgain" : 42.1
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
"strings" : {
|
|
13
|
+
"abcd" : "abcd",
|
|
14
|
+
"abcdAgain" : "abcd",
|
|
15
|
+
"a" : "a",
|
|
16
|
+
"b" : "b",
|
|
17
|
+
"c" : "c",
|
|
18
|
+
"d" : "d",
|
|
19
|
+
"concatenated" : "null bar 42 baz true 3.14 hi"
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
"arrays" : {
|
|
23
|
+
"empty" : [],
|
|
24
|
+
"1" : [ 1 ],
|
|
25
|
+
"12" : [1, 2],
|
|
26
|
+
"123" : [1, 2, 3],
|
|
27
|
+
"ofString" : [ "a", "b", "c" ]
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
"booleans" : {
|
|
31
|
+
"true" : true,
|
|
32
|
+
"trueAgain" : true,
|
|
33
|
+
"false" : false,
|
|
34
|
+
"falseAgain" : false
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
"nulls" : {
|
|
38
|
+
"null" : null,
|
|
39
|
+
"nullAgain" : null
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
ints.fortyTwo : 42,
|
|
3
|
+
ints.fortyTwoAgain : 42,
|
|
4
|
+
|
|
5
|
+
floats.fortyTwoPointOne : 42.1,
|
|
6
|
+
floats.fortyTwoPointOneAgain : 42.1,
|
|
7
|
+
|
|
8
|
+
strings.abcd : "abcd",
|
|
9
|
+
strings.abcdAgain : "abcd",
|
|
10
|
+
strings.a : "a",
|
|
11
|
+
strings.b : "b",
|
|
12
|
+
strings.c : "c",
|
|
13
|
+
strings.d : "d",
|
|
14
|
+
strings.concatenated : "null bar 42 baz true 3.14 hi",
|
|
15
|
+
|
|
16
|
+
arrays."empty" : [],
|
|
17
|
+
arrays."1" : [ 1 ],
|
|
18
|
+
arrays.12 : [1, 2],
|
|
19
|
+
arrays.123 : [1, 2, 3],
|
|
20
|
+
arrays.ofString : [ "a", "b", "c" ],
|
|
21
|
+
|
|
22
|
+
booleans.true : true,
|
|
23
|
+
booleans.trueAgain : true,
|
|
24
|
+
booleans.false : false,
|
|
25
|
+
booleans.falseAgain : false,
|
|
26
|
+
|
|
27
|
+
nulls.null : null,
|
|
28
|
+
nulls.nullAgain : null
|
|
29
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# this file is in a no-braces java properties style,
|
|
2
|
+
# but it's a .conf file with json-style escaping,
|
|
3
|
+
# not a Java properties file.
|
|
4
|
+
# lots of playing with whitespace and commas
|
|
5
|
+
# in here to test various cases; don't copy
|
|
6
|
+
# this as exemplary, it has gratuitously
|
|
7
|
+
# stupid formatting.
|
|
8
|
+
ints.fortyTwo=42
|
|
9
|
+
ints.fortyTwoAgain = 42 // whitespace ignored around the =
|
|
10
|
+
floats.fortyTwoPointOne=42.1
|
|
11
|
+
floats.fortyTwoPointOneAgain:42.1 // colon is fine too
|
|
12
|
+
strings.abcd=abcd
|
|
13
|
+
strings.abcdAgain : "abcd", // colon and quotes and comma!
|
|
14
|
+
strings.a=a
|
|
15
|
+
strings.b=b
|
|
16
|
+
strings.c="c",
|
|
17
|
+
strings.d:"d"
|
|
18
|
+
strings.concatenated=null bar 42 baz true 3.14 hi # this gets squished to a string
|
|
19
|
+
arrays.empty=[]
|
|
20
|
+
arrays.1 : [ 1 ]
|
|
21
|
+
# it's allowed to omit commas in arrays if you have newlines
|
|
22
|
+
arrays.12 = [
|
|
23
|
+
1
|
|
24
|
+
2
|
|
25
|
+
]
|
|
26
|
+
arrays.123 = [1,
|
|
27
|
+
2
|
|
28
|
+
3]
|
|
29
|
+
arrays.ofString = [ "a", "b", "c" ]
|
|
30
|
+
booleans.true=true
|
|
31
|
+
booleans.trueAgain=${booleans.true}
|
|
32
|
+
booleans.false=false
|
|
33
|
+
# you could quote keys, though here it's pointless
|
|
34
|
+
# since the key contains no special characters
|
|
35
|
+
"booleans"."falseAgain" : ${booleans.false},
|
|
36
|
+
nulls.null=${nulls.nullAgain}
|
|
37
|
+
nulls.nullAgain=null
|