yini-cli 1.0.0-alpha.2
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/workflows/publish.yml +30 -0
- package/.github/workflows/run-all-tests.yml +51 -0
- package/.github/workflows/run-smoke-tests.yml +33 -0
- package/.nvmrc +1 -0
- package/.vscode/settings.json +3 -0
- package/CONTRIBUTING.md +6 -0
- package/README.md +68 -0
- package/dist/commands/info.d.ts +1 -0
- package/dist/commands/parse.d.ts +2 -0
- package/dist/commands/validate.d.ts +7 -0
- package/dist/config/env.d.ts +34 -0
- package/dist/descriptions.d.ts +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +162 -0
- package/dist/types.d.ts +8 -0
- package/dist/utils/print.d.ts +14 -0
- package/docs/contributing.md +28 -0
- package/docs/project-setup.md +32 -0
- package/eslint.config.js +40 -0
- package/package.json +93 -0
- package/prettier.config.cjs +33 -0
- package/sample.yini +19 -0
- package/samples/basic.yini +11 -0
- package/samples/nested.yini +26 -0
- package/src/commands/info.ts +19 -0
- package/src/commands/parse.ts +88 -0
- package/src/commands/validate.ts +48 -0
- package/src/config/env.ts +85 -0
- package/src/descriptions.ts +6 -0
- package/src/index.ts +182 -0
- package/src/types.ts +9 -0
- package/src/utils/print.ts +49 -0
- package/tests/fixtures/corrupt-config-1.yini +8 -0
- package/tests/fixtures/invalid-config-1.yini +2 -0
- package/tests/fixtures/nested-config-1.yini +7 -0
- package/tests/fixtures/valid-config-1.yini +5 -0
- package/tests/general.test.ts +86 -0
- package/tests/smoke.test.ts +145 -0
- package/tests/test-helpers.ts +9 -0
- package/tsconfig.json +16 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* General Tests.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// import pkg from '../package.json'
|
|
6
|
+
import { createRequire } from 'module'
|
|
7
|
+
import fs from 'node:fs'
|
|
8
|
+
import path from 'node:path'
|
|
9
|
+
import { describe, expect, it } from 'vitest'
|
|
10
|
+
import { descriptions as descripts } from '../src/descriptions.js'
|
|
11
|
+
import {
|
|
12
|
+
debugPrint,
|
|
13
|
+
printObject,
|
|
14
|
+
toJSON,
|
|
15
|
+
toPrettyJSON,
|
|
16
|
+
} from '../src/utils/print.js'
|
|
17
|
+
import { yiniCLI } from './test-helpers.js'
|
|
18
|
+
|
|
19
|
+
const require = createRequire(import.meta.url)
|
|
20
|
+
const pkg = require('../package.json')
|
|
21
|
+
|
|
22
|
+
// import pkg from '../package.json' assert { type: 'json' }
|
|
23
|
+
|
|
24
|
+
const DIR_OF_FIXTURES = 'fixtures/'
|
|
25
|
+
|
|
26
|
+
describe('Test general yini CLI usage:', () => {
|
|
27
|
+
const baseDir = path.join(__dirname, DIR_OF_FIXTURES)
|
|
28
|
+
|
|
29
|
+
it('1.a) The yini command "--version" correctly outputs version.', async () => {
|
|
30
|
+
// Arrange.
|
|
31
|
+
// const fileName = 'valid-config-1.yini'
|
|
32
|
+
// const fullPath = path.join(baseDir, fileName)
|
|
33
|
+
|
|
34
|
+
// Arrange and Act.
|
|
35
|
+
// Here, execa(..) is used to run CLI script (like tsx src/index.ts myfile.yini),
|
|
36
|
+
// and to capture stdout, stderr, exitCode, etc.
|
|
37
|
+
//const { stdout } = await yiniCLI(`parse ${fullPath}`)
|
|
38
|
+
const { stdout } = await yiniCLI(`--version`)
|
|
39
|
+
debugPrint('Test: 1:')
|
|
40
|
+
debugPrint('stdout:')
|
|
41
|
+
printObject(stdout)
|
|
42
|
+
|
|
43
|
+
// Assert.
|
|
44
|
+
//expect(stdout).toContain('{ App:')
|
|
45
|
+
expect(stdout.trim()).equal(pkg.version)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('1.b) The yini command "-V" (uppecase) correctly outputs version.', async () => {
|
|
49
|
+
// Arrange and Act.
|
|
50
|
+
const { stdout } = await yiniCLI(`-V`)
|
|
51
|
+
debugPrint('Test: 1:')
|
|
52
|
+
debugPrint('stdout:')
|
|
53
|
+
printObject(stdout)
|
|
54
|
+
|
|
55
|
+
// Assert.
|
|
56
|
+
expect(stdout.trim()).equal(pkg.version)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('2.a) The yini command "--help" correctly outputs help/usage message.', async () => {
|
|
60
|
+
// Arrange and Act.
|
|
61
|
+
const { stdout } = await yiniCLI(`--help`)
|
|
62
|
+
debugPrint('Test: 1:')
|
|
63
|
+
debugPrint('stdout:')
|
|
64
|
+
printObject(stdout)
|
|
65
|
+
|
|
66
|
+
// Assert.
|
|
67
|
+
expect(stdout).contain(descripts.yini)
|
|
68
|
+
expect(stdout).contain(descripts['For-command-info'])
|
|
69
|
+
expect(stdout).contain(descripts['For-command-parse'])
|
|
70
|
+
expect(stdout).contain(descripts['For-command-validate'])
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('2.b) The yini command "-h" correctly outputs help/usage message.', async () => {
|
|
74
|
+
// Arrange and Act.
|
|
75
|
+
const { stdout } = await yiniCLI(`-h`)
|
|
76
|
+
debugPrint('Test: 1:')
|
|
77
|
+
debugPrint('stdout:')
|
|
78
|
+
printObject(stdout)
|
|
79
|
+
|
|
80
|
+
// Assert.
|
|
81
|
+
expect(stdout).contain(descripts.yini)
|
|
82
|
+
expect(stdout).contain(descripts['For-command-info'])
|
|
83
|
+
expect(stdout).contain(descripts['For-command-parse'])
|
|
84
|
+
expect(stdout).contain(descripts['For-command-validate'])
|
|
85
|
+
})
|
|
86
|
+
})
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smoke Tests.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'node:fs'
|
|
6
|
+
import path from 'node:path'
|
|
7
|
+
import { describe, expect, it } from 'vitest'
|
|
8
|
+
import {
|
|
9
|
+
debugPrint,
|
|
10
|
+
printObject,
|
|
11
|
+
toJSON,
|
|
12
|
+
toPrettyJSON,
|
|
13
|
+
} from '../src/utils/print'
|
|
14
|
+
import { yiniCLI } from './test-helpers'
|
|
15
|
+
|
|
16
|
+
const DIR_OF_FIXTURES = 'fixtures/'
|
|
17
|
+
|
|
18
|
+
describe('Test yini CLI basic usage:', () => {
|
|
19
|
+
const baseDir = path.join(__dirname, DIR_OF_FIXTURES)
|
|
20
|
+
|
|
21
|
+
it('1. Parses a valid YINI file and print as JS object.', async () => {
|
|
22
|
+
// Arrange.
|
|
23
|
+
const fileName = 'valid-config-1.yini'
|
|
24
|
+
const fullPath = path.join(baseDir, fileName)
|
|
25
|
+
|
|
26
|
+
// Act.
|
|
27
|
+
// Here, execa(..) is used to run CLI script (like tsx src/index.ts myfile.yini),
|
|
28
|
+
// and to capture stdout, stderr, exitCode, etc.
|
|
29
|
+
const { stdout } = await yiniCLI(`parse ${fullPath}`)
|
|
30
|
+
debugPrint('Test: 1:')
|
|
31
|
+
debugPrint('stdout:')
|
|
32
|
+
printObject(stdout)
|
|
33
|
+
|
|
34
|
+
// Assert.
|
|
35
|
+
expect(stdout).toContain('{ App:')
|
|
36
|
+
expect(stdout).toContain('title: ')
|
|
37
|
+
expect(stdout).toContain('My App')
|
|
38
|
+
expect(stdout).toContain('enabled: true')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('2. Parses nested sections and print as JS object.', async () => {
|
|
42
|
+
// Arrange.
|
|
43
|
+
const fileName = 'nested-config-1.yini'
|
|
44
|
+
const fullPath = path.join(baseDir, fileName)
|
|
45
|
+
|
|
46
|
+
// Act.
|
|
47
|
+
const { stdout } = await yiniCLI(`parse ${fullPath}`)
|
|
48
|
+
debugPrint('Test: 2:')
|
|
49
|
+
debugPrint('stdout:')
|
|
50
|
+
printObject(stdout)
|
|
51
|
+
|
|
52
|
+
// Assert.
|
|
53
|
+
expect(stdout).toContain('{ App:')
|
|
54
|
+
expect(stdout).toContain("name: 'Nested'")
|
|
55
|
+
expect(stdout).toContain('Database: {')
|
|
56
|
+
expect(stdout).toContain("host: 'localhost'")
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('3. Shows error on invalid YINI that has some garbage.', async () => {
|
|
60
|
+
// Arrange.
|
|
61
|
+
const fileName = 'invalid-config-1.yini'
|
|
62
|
+
const fullPath = path.join(baseDir, fileName)
|
|
63
|
+
|
|
64
|
+
// Act.
|
|
65
|
+
const { stderr, exitCode } = await yiniCLI(`parse ${fullPath}`)
|
|
66
|
+
debugPrint('Test: 3:')
|
|
67
|
+
debugPrint('stderr:')
|
|
68
|
+
printObject(stderr)
|
|
69
|
+
debugPrint('exitCode:')
|
|
70
|
+
printObject(exitCode)
|
|
71
|
+
|
|
72
|
+
// Assert.
|
|
73
|
+
expect(exitCode).not.toBe(0)
|
|
74
|
+
expect(stderr.toLowerCase()).toContain('syntax error')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('4. Parse and print as pretty JSON.', async () => {
|
|
78
|
+
// Arrange.
|
|
79
|
+
const fileName = 'nested-config-1.yini'
|
|
80
|
+
const fullPath = path.join(baseDir, fileName)
|
|
81
|
+
|
|
82
|
+
// Act.
|
|
83
|
+
const { stdout } = await yiniCLI(`parse ${fullPath} --pretty`)
|
|
84
|
+
debugPrint('Test: 2:')
|
|
85
|
+
debugPrint('stdout:')
|
|
86
|
+
printObject(stdout)
|
|
87
|
+
|
|
88
|
+
// Assert.
|
|
89
|
+
expect(stdout).toContain(' "App": {')
|
|
90
|
+
expect(stdout).toContain(' "name": "Nested",')
|
|
91
|
+
expect(stdout).toContain(' "Database": {')
|
|
92
|
+
expect(stdout).toContain(' "host": "localhost"')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('5. Parse and print as JSON string.', async () => {
|
|
96
|
+
// Arrange.
|
|
97
|
+
const fileName = 'nested-config-1.yini'
|
|
98
|
+
const fullPath = path.join(baseDir, fileName)
|
|
99
|
+
|
|
100
|
+
// Act.
|
|
101
|
+
const { stdout } = await yiniCLI(`parse ${fullPath} --json`)
|
|
102
|
+
debugPrint('Test: 2:')
|
|
103
|
+
debugPrint('stdout:')
|
|
104
|
+
printObject(stdout)
|
|
105
|
+
|
|
106
|
+
// Assert.
|
|
107
|
+
expect(stdout).toContain(
|
|
108
|
+
'{"App":{"name":"Nested","Database":{"host":"localhost"}}}',
|
|
109
|
+
)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('6.a. Should pass parsing corrupt YINI in lenient (default) mode.', async () => {
|
|
113
|
+
// Arrange.
|
|
114
|
+
const fileName = 'corrupt-config-1.yini'
|
|
115
|
+
const fullPath = path.join(baseDir, fileName)
|
|
116
|
+
|
|
117
|
+
// Act.
|
|
118
|
+
const { stdout } = await yiniCLI(`parse ${fullPath} --json`)
|
|
119
|
+
debugPrint('Test: 6.a:')
|
|
120
|
+
debugPrint('stdout:')
|
|
121
|
+
printObject(stdout)
|
|
122
|
+
|
|
123
|
+
// Assert.
|
|
124
|
+
const jsObj = { Section: { value: 42 } }
|
|
125
|
+
expect(stdout).toContain(toJSON(jsObj))
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it.skip('6.b. Show error on corrupt YINI in strict-mode.', async () => {
|
|
129
|
+
// Arrange.
|
|
130
|
+
const fileName = 'corrupt-config-1.yini'
|
|
131
|
+
const fullPath = path.join(baseDir, fileName)
|
|
132
|
+
|
|
133
|
+
// Act.
|
|
134
|
+
const { stderr, exitCode } = await yiniCLI(`parse ${fullPath} strict`)
|
|
135
|
+
debugPrint('Test: 6.b:')
|
|
136
|
+
debugPrint('stderr:')
|
|
137
|
+
printObject(stderr)
|
|
138
|
+
debugPrint('exitCode:')
|
|
139
|
+
printObject(exitCode)
|
|
140
|
+
|
|
141
|
+
// Assert.
|
|
142
|
+
expect(exitCode).not.toBe(0)
|
|
143
|
+
expect(stderr.toLowerCase()).toContain('syntax error')
|
|
144
|
+
})
|
|
145
|
+
})
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { execa } from 'execa'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param argsLine A line of arguments (command and options) to pass to yini
|
|
5
|
+
* (yini-cli) on execution in CLI.
|
|
6
|
+
* @returns
|
|
7
|
+
*/
|
|
8
|
+
export const yiniCLI = (argsLine: string) =>
|
|
9
|
+
execa('tsx', ['src/index.ts', ...argsLine.split(' ')], { reject: false })
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
},
|
|
15
|
+
"include": ["src"]
|
|
16
|
+
}
|