@o3co/ts.hocon 0.1.0 → 0.1.1

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.
Files changed (106) hide show
  1. package/README.ja.md +49 -0
  2. package/README.md +49 -0
  3. package/dist/config-D4EyvNd8.d.cts +31 -0
  4. package/dist/config-D4EyvNd8.d.ts +31 -0
  5. package/dist/index.cjs +883 -0
  6. package/dist/index.d.cts +39 -0
  7. package/dist/index.d.ts +39 -0
  8. package/dist/index.js +839 -0
  9. package/dist/zod.cjs +39 -0
  10. package/dist/zod.d.cts +7 -0
  11. package/dist/zod.d.ts +7 -0
  12. package/dist/zod.js +13 -0
  13. package/package.json +13 -10
  14. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -29
  15. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -23
  16. package/.github/pull_request_template.md +0 -17
  17. package/.github/workflows/lint.yml +0 -33
  18. package/.github/workflows/release.yml +0 -41
  19. package/.github/workflows/test.yml +0 -53
  20. package/CHANGELOG.md +0 -22
  21. package/CONTRIBUTING.md +0 -62
  22. package/SECURITY.md +0 -20
  23. package/docs/superpowers/plans/2026-03-20-ts-hocon.md +0 -2412
  24. package/docs/superpowers/specs/2026-03-20-ts-hocon-design.md +0 -368
  25. package/src/config.ts +0 -108
  26. package/src/errors.ts +0 -34
  27. package/src/index.ts +0 -5
  28. package/src/internal/lexer/lexer.ts +0 -168
  29. package/src/internal/lexer/token.ts +0 -20
  30. package/src/internal/parser/ast.ts +0 -17
  31. package/src/internal/parser/parser.ts +0 -229
  32. package/src/internal/resolver/resolver.ts +0 -428
  33. package/src/parse.ts +0 -79
  34. package/src/value.ts +0 -5
  35. package/src/zod.ts +0 -12
  36. package/tests/config.test.ts +0 -100
  37. package/tests/errors.test.ts +0 -43
  38. package/tests/lexer.test.ts +0 -141
  39. package/tests/lightbend/lightbend.test.ts +0 -39
  40. package/tests/lightbend/testdata/a_1.conf +0 -1
  41. package/tests/lightbend/testdata/b_2.conf +0 -1
  42. package/tests/lightbend/testdata/beanconfig/beanconfig01.conf +0 -131
  43. package/tests/lightbend/testdata/bom.conf +0 -2
  44. package/tests/lightbend/testdata/cycle.conf +0 -1
  45. package/tests/lightbend/testdata/env-variables.conf +0 -16
  46. package/tests/lightbend/testdata/equiv01/comments.conf +0 -45
  47. package/tests/lightbend/testdata/equiv01/equals.conf +0 -41
  48. package/tests/lightbend/testdata/equiv01/no-commas.conf +0 -55
  49. package/tests/lightbend/testdata/equiv01/no-root-braces.conf +0 -40
  50. package/tests/lightbend/testdata/equiv01/no-whitespace.json +0 -1
  51. package/tests/lightbend/testdata/equiv01/omit-colons.conf +0 -44
  52. package/tests/lightbend/testdata/equiv01/original.json +0 -41
  53. package/tests/lightbend/testdata/equiv01/path-keys.conf +0 -29
  54. package/tests/lightbend/testdata/equiv01/properties-style.conf +0 -37
  55. package/tests/lightbend/testdata/equiv01/substitutions.conf +0 -41
  56. package/tests/lightbend/testdata/equiv01/unquoted.conf +0 -41
  57. package/tests/lightbend/testdata/equiv02/original.json +0 -17
  58. package/tests/lightbend/testdata/equiv02/path-keys-weird-whitespace.conf +0 -32
  59. package/tests/lightbend/testdata/equiv02/path-keys.conf +0 -9
  60. package/tests/lightbend/testdata/equiv03/includes.conf +0 -7
  61. package/tests/lightbend/testdata/equiv03/letters/a.conf +0 -6
  62. package/tests/lightbend/testdata/equiv03/letters/b.json +0 -3
  63. package/tests/lightbend/testdata/equiv03/letters/c.conf +0 -2
  64. package/tests/lightbend/testdata/equiv03/letters/c.properties +0 -1
  65. package/tests/lightbend/testdata/equiv03/letters/numbers/1.conf +0 -1
  66. package/tests/lightbend/testdata/equiv03/letters/numbers/2.properties +0 -1
  67. package/tests/lightbend/testdata/equiv03/original.json +0 -15
  68. package/tests/lightbend/testdata/equiv03/root/foo.conf +0 -1
  69. package/tests/lightbend/testdata/equiv04/missing-substitutions.conf +0 -4
  70. package/tests/lightbend/testdata/equiv04/original.json +0 -3
  71. package/tests/lightbend/testdata/equiv05/original.json +0 -16
  72. package/tests/lightbend/testdata/equiv05/triple-quotes.conf +0 -20
  73. package/tests/lightbend/testdata/file-include.conf +0 -5
  74. package/tests/lightbend/testdata/include-from-list.conf +0 -5
  75. package/tests/lightbend/testdata/onclasspath.conf +0 -2
  76. package/tests/lightbend/testdata/subdir/bar-file.conf +0 -1
  77. package/tests/lightbend/testdata/subdir/bar.conf +0 -1
  78. package/tests/lightbend/testdata/subdir/baz.conf +0 -1
  79. package/tests/lightbend/testdata/subdir/foo.conf +0 -5
  80. package/tests/lightbend/testdata/test01.conf +0 -96
  81. package/tests/lightbend/testdata/test01.json +0 -4
  82. package/tests/lightbend/testdata/test01.properties +0 -5
  83. package/tests/lightbend/testdata/test02.conf +0 -13
  84. package/tests/lightbend/testdata/test03-included.conf +0 -9
  85. package/tests/lightbend/testdata/test03.conf +0 -36
  86. package/tests/lightbend/testdata/test04.conf +0 -305
  87. package/tests/lightbend/testdata/test05.conf +0 -154
  88. package/tests/lightbend/testdata/test06.conf +0 -14
  89. package/tests/lightbend/testdata/test07.conf +0 -4
  90. package/tests/lightbend/testdata/test08.conf +0 -6
  91. package/tests/lightbend/testdata/test09.conf +0 -17
  92. package/tests/lightbend/testdata/test10.conf +0 -11
  93. package/tests/lightbend/testdata/test11.conf +0 -7
  94. package/tests/lightbend/testdata/test12.conf +0 -43
  95. package/tests/lightbend/testdata/test13-application-override-substitutions.conf +0 -1
  96. package/tests/lightbend/testdata/test13-reference-bad-substitutions.conf +0 -1
  97. package/tests/lightbend/testdata/test13-reference-with-substitutions.conf +0 -2
  98. package/tests/lightbend/testdata/validate-invalid.conf +0 -30
  99. package/tests/lightbend/testdata/validate-reference.conf +0 -32
  100. package/tests/parse.test.ts +0 -44
  101. package/tests/parser.test.ts +0 -154
  102. package/tests/resolver.test.ts +0 -159
  103. package/tests/zod.test.ts +0 -57
  104. package/tsconfig.json +0 -16
  105. package/tsup.config.ts +0 -12
  106. package/vitest.config.ts +0 -7
package/src/parse.ts DELETED
@@ -1,79 +0,0 @@
1
- import { tokenize } from './internal/lexer/lexer.js'
2
- import { parseTokens } from './internal/parser/parser.js'
3
- import { resolve } from './internal/resolver/resolver.js'
4
- import { Config } from './config.js'
5
- import type { ResolveOptions } from './internal/resolver/resolver.js'
6
- import * as fs from 'node:fs'
7
- import * as path from 'node:path'
8
-
9
- export type ParseOptions = {
10
- baseDir?: string
11
- env?: Record<string, string>
12
- readFile?: (filePath: string) => Promise<string>
13
- readFileSync?: (filePath: string) => string
14
- }
15
-
16
- function getEnv(opts: ParseOptions): Record<string, string> {
17
- if (opts.env !== undefined) return opts.env
18
- if (typeof process !== 'undefined' && process.env) return process.env as Record<string, string>
19
- return {}
20
- }
21
-
22
- function defaultReadFileSync(filePath: string): string {
23
- return fs.readFileSync(filePath, 'utf-8')
24
- }
25
-
26
- async function defaultReadFile(filePath: string): Promise<string> {
27
- return fs.promises.readFile(filePath, 'utf-8')
28
- }
29
-
30
- export function parse(input: string, opts: ParseOptions = {}): Config {
31
- const tokens = tokenize(input)
32
- const ast = parseTokens(tokens)
33
- const resolveOpts: ResolveOptions = {
34
- env: getEnv(opts),
35
- baseDir: opts.baseDir,
36
- readFileSync: opts.readFileSync ?? defaultReadFileSync,
37
- }
38
- const value = resolve(ast, resolveOpts)
39
- if (value.kind !== 'object') throw new Error('resolved value is not an object')
40
- return new Config(value)
41
- }
42
-
43
- /**
44
- * Async wrapper around `parse()`. Returns a resolved Promise — the parsing
45
- * itself is synchronous. Use `parseFileAsync()` for async file I/O.
46
- * Note: `include` directives in string input are resolved synchronously
47
- * via `readFileSync`; `readFile` is not used by this function.
48
- */
49
- export async function parseAsync(input: string, opts: ParseOptions = {}): Promise<Config> {
50
- return Promise.resolve(parse(input, opts))
51
- }
52
-
53
- export function parseFile(filePath: string, opts: ParseOptions = {}): Config {
54
- if (typeof process === 'undefined' && !opts.readFileSync) {
55
- throw new Error('parseFile is not supported in browser environments. Provide opts.readFileSync or use parse() instead.')
56
- }
57
- const resolvedPath = path.resolve(filePath)
58
- const readFileSync = opts.readFileSync ?? defaultReadFileSync
59
- const input = readFileSync(resolvedPath)
60
- return parse(input, {
61
- ...opts,
62
- baseDir: opts.baseDir ?? path.dirname(resolvedPath),
63
- readFileSync,
64
- })
65
- }
66
-
67
- export async function parseFileAsync(filePath: string, opts: ParseOptions = {}): Promise<Config> {
68
- if (typeof process === 'undefined' && !opts.readFile) {
69
- throw new Error('parseFileAsync is not supported in browser environments. Provide opts.readFile or use parseAsync() instead.')
70
- }
71
- const resolvedPath = path.resolve(filePath)
72
- const readFile = opts.readFile ?? defaultReadFile
73
- const input = await readFile(resolvedPath)
74
- return parse(input, {
75
- ...opts,
76
- baseDir: opts.baseDir ?? path.dirname(resolvedPath),
77
- readFileSync: opts.readFileSync ?? defaultReadFileSync,
78
- })
79
- }
package/src/value.ts DELETED
@@ -1,5 +0,0 @@
1
- // JavaScript の Map は挿入順を保証するため keys フィールド不要
2
- export type HoconValue =
3
- | { kind: 'object'; fields: Map<string, HoconValue> }
4
- | { kind: 'array'; items: HoconValue[] }
5
- | { kind: 'scalar'; value: string | number | boolean | null }
package/src/zod.ts DELETED
@@ -1,12 +0,0 @@
1
- import type { ZodType } from 'zod'
2
- import type { Config } from './config.js'
3
-
4
- export function validate<T>(config: Config, schema: ZodType<T>): T {
5
- const plain = config.toObject()
6
- return schema.parse(plain)
7
- }
8
-
9
- export function getValidated<T>(config: Config, path: string, schema: ZodType<T>): T {
10
- const val = config.get(path)
11
- return schema.parse(val)
12
- }
@@ -1,100 +0,0 @@
1
- // tests/config.test.ts
2
- import { describe, it, expect } from 'vitest'
3
- import { Config } from '../src/config.js'
4
- import type { HoconValue } from '../src/value.js'
5
- import { ConfigError } from '../src/errors.js'
6
-
7
- function makeConfig(obj: Record<string, HoconValue>): Config {
8
- return new Config({ kind: 'object', fields: new Map(Object.entries(obj)) })
9
- }
10
-
11
- describe('Config', () => {
12
- it('get() returns value at path', () => {
13
- const c = makeConfig({ host: { kind: 'scalar', value: 'localhost' } })
14
- expect(c.get('host')).toBe('localhost')
15
- })
16
-
17
- it('get() returns nested value at dot-path', () => {
18
- const inner = new Map([['host', { kind: 'scalar', value: 'localhost' } satisfies HoconValue]])
19
- const c = new Config({ kind: 'object', fields: new Map([['server', { kind: 'object', fields: inner }]]) })
20
- expect(c.get('server.host')).toBe('localhost')
21
- })
22
-
23
- it('get() returns undefined for missing path', () => {
24
- const c = makeConfig({})
25
- expect(c.get('missing')).toBeUndefined()
26
- })
27
-
28
- it('getString() returns string value', () => {
29
- const c = makeConfig({ host: { kind: 'scalar', value: 'localhost' } })
30
- expect(c.getString('host')).toBe('localhost')
31
- })
32
-
33
- it('getString() throws ConfigError for non-string', () => {
34
- const c = makeConfig({ port: { kind: 'scalar', value: 8080 } })
35
- expect(() => c.getString('port')).toThrow(ConfigError)
36
- })
37
-
38
- it('getNumber() returns number value', () => {
39
- const c = makeConfig({ port: { kind: 'scalar', value: 8080 } })
40
- expect(c.getNumber('port')).toBe(8080)
41
- })
42
-
43
- it('getNumber() throws ConfigError for non-number', () => {
44
- const c = makeConfig({ host: { kind: 'scalar', value: 'localhost' } })
45
- expect(() => c.getNumber('host')).toThrow(ConfigError)
46
- })
47
-
48
- it('getBoolean() returns boolean value', () => {
49
- const c = makeConfig({ debug: { kind: 'scalar', value: true } })
50
- expect(c.getBoolean('debug')).toBe(true)
51
- })
52
-
53
- it('getConfig() returns sub-config', () => {
54
- const inner = new Map([['host', { kind: 'scalar', value: 'localhost' } satisfies HoconValue]])
55
- const c = new Config({ kind: 'object', fields: new Map([['server', { kind: 'object', fields: inner }]]) })
56
- const sub = c.getConfig('server')
57
- expect(sub).toBeInstanceOf(Config)
58
- expect(sub.getString('host')).toBe('localhost')
59
- })
60
-
61
- it('getList() returns array as unknown[]', () => {
62
- const items: HoconValue[] = [{ kind: 'scalar', value: 1 }, { kind: 'scalar', value: 2 }]
63
- const c = makeConfig({ list: { kind: 'array', items } })
64
- expect(c.getList('list')).toEqual([1, 2])
65
- })
66
-
67
- it('has() returns true for existing key', () => {
68
- const c = makeConfig({ host: { kind: 'scalar', value: 'localhost' } })
69
- expect(c.has('host')).toBe(true)
70
- })
71
-
72
- it('has() returns true for null value', () => {
73
- const c = makeConfig({ x: { kind: 'scalar', value: null } })
74
- expect(c.has('x')).toBe(true)
75
- })
76
-
77
- it('has() returns false for missing key', () => {
78
- const c = makeConfig({})
79
- expect(c.has('missing')).toBe(false)
80
- })
81
-
82
- it('keys() returns top-level keys in order', () => {
83
- const c = new Config({ kind: 'object', fields: new Map([['b', { kind: 'scalar', value: 2 }], ['a', { kind: 'scalar', value: 1 }]]) })
84
- expect(c.keys()).toEqual(['b', 'a'])
85
- })
86
-
87
- it('withFallback() receiver wins', () => {
88
- const c1 = makeConfig({ host: { kind: 'scalar', value: 'prod' } })
89
- const c2 = makeConfig({ host: { kind: 'scalar', value: 'dev' }, port: { kind: 'scalar', value: 8080 } })
90
- const merged = c1.withFallback(c2)
91
- expect(merged.getString('host')).toBe('prod')
92
- expect(merged.getNumber('port')).toBe(8080)
93
- })
94
-
95
- it('toObject() converts to plain JS object recursively', () => {
96
- const inner = new Map([['host', { kind: 'scalar', value: 'localhost' } satisfies HoconValue]])
97
- const c = new Config({ kind: 'object', fields: new Map([['server', { kind: 'object', fields: inner }]]) })
98
- expect(c.toObject()).toEqual({ server: { host: 'localhost' } })
99
- })
100
- })
@@ -1,43 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { ParseError, ResolveError, ConfigError } from '../src/errors.js'
3
-
4
- describe('ParseError', () => {
5
- it('is an Error with line/col', () => {
6
- const e = new ParseError('unexpected token', 3, 10)
7
- expect(e).toBeInstanceOf(Error)
8
- expect(e.message).toBe('unexpected token')
9
- expect(e.line).toBe(3)
10
- expect(e.col).toBe(10)
11
- expect(e.file).toBeUndefined()
12
- })
13
-
14
- it('accepts optional file path', () => {
15
- const e = new ParseError('bad syntax', 1, 1, 'app.conf')
16
- expect(e.file).toBe('app.conf')
17
- })
18
- })
19
-
20
- describe('ResolveError', () => {
21
- it('carries path and position', () => {
22
- const e = new ResolveError('unresolved substitution', 'server.host', 5, 3)
23
- expect(e).toBeInstanceOf(Error)
24
- expect(e.path).toBe('server.host')
25
- expect(e.line).toBe(5)
26
- expect(e.col).toBe(3)
27
- })
28
-
29
- it('line/col can be 0 (no position info)', () => {
30
- const e = new ResolveError('include failed', 'path', 0, 0, 'app.conf')
31
- expect(e.line).toBe(0)
32
- expect(e.col).toBe(0)
33
- expect(e.file).toBe('app.conf')
34
- })
35
- })
36
-
37
- describe('ConfigError', () => {
38
- it('carries path', () => {
39
- const e = new ConfigError('key not found', 'server.port')
40
- expect(e).toBeInstanceOf(Error)
41
- expect(e.path).toBe('server.port')
42
- })
43
- })
@@ -1,141 +0,0 @@
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
- })
@@ -1,39 +0,0 @@
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
- })
@@ -1 +0,0 @@
1
- a=1
@@ -1 +0,0 @@
1
- b=2
@@ -1,131 +0,0 @@
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
- }
@@ -1,2 +0,0 @@
1
- #
2
- foo = bar
@@ -1 +0,0 @@
1
- include "cycle.conf"
@@ -1,16 +0,0 @@
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[]}
@@ -1,45 +0,0 @@
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
- }
@@ -1,41 +0,0 @@
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
- }