yini-cli 1.0.3-beta → 1.1.1-beta
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/README.md +134 -108
- package/dist/commands/parseCommand.d.ts +7 -0
- package/dist/commands/{parse.js → parseCommand.js} +25 -24
- package/dist/commands/validateCommand.d.ts +6 -0
- package/dist/commands/validateCommand.js +217 -0
- package/dist/descriptions.js +1 -1
- package/dist/globalOptions/helpOption.d.ts +8 -0
- package/dist/globalOptions/helpOption.js +52 -0
- package/dist/{commands/info.js → globalOptions/infoOption.js} +2 -1
- package/dist/index.js +100 -129
- package/dist/types.d.ts +4 -6
- package/dist/types.js +4 -0
- package/dist/utils/print.d.ts +3 -4
- package/dist/utils/print.js +11 -8
- package/dist/utils/yiniCliHelpers.d.ts +2 -0
- package/dist/utils/yiniCliHelpers.js +13 -0
- package/package.json +9 -5
- package/sample.yini +2 -0
- package/dist/commands/parse.d.ts +0 -2
- package/dist/commands/validate.d.ts +0 -7
- package/dist/commands/validate.js +0 -32
- /package/dist/{commands/info.d.ts → globalOptions/infoOption.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,54 +1,130 @@
|
|
|
1
1
|
# YINI-CLI
|
|
2
|
-
**Command-line tool for
|
|
2
|
+
**Command-line tool for validating, inspecting, and converting YINI configuration files to JSON.**
|
|
3
3
|
|
|
4
|
-
*YINI
|
|
4
|
+
*YINI is an INI-inspired configuration format designed for clarity and predictability. It supports nesting, comments, and a formally defined syntax, so configuration files stay easy to read and reason about as they grow.*
|
|
5
5
|
|
|
6
|
-
[](https://www.npmjs.com/package/yini-cli) [](https://github.com/YINI-lang/yini-cli/actions/workflows/run-all-tests.yml) [](https://github.com/YINI-lang/yini-cli/actions/workflows/run-regression-tests.yml) [](https://github.com/YINI-lang/yini-cli/actions/workflows/run-cli-test.yml)
|
|
7
|
+
|
|
8
|
+
This tool is useful if you work with human-edited configuration files and want predictable structure without indentation-based rules.
|
|
7
9
|
|
|
8
10
|
---
|
|
9
11
|
|
|
10
|
-
##
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
### Requirements
|
|
15
|
+
YINI CLI requires Node.js **v20 or later**.
|
|
16
|
+
|
|
17
|
+
(It has also been tested with Node.js v13+, but v20+ is recommended for best compatibility.)
|
|
18
|
+
|
|
19
|
+
### Installation
|
|
20
|
+
|
|
21
|
+
1. **Install it globally from npm — (requires Node.js)**
|
|
22
|
+
Open your terminal and run:
|
|
23
|
+
```
|
|
24
|
+
npm install -g yini-cli
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
2. **Verify installation**
|
|
28
|
+
Run this in your terminal:
|
|
29
|
+
```bash
|
|
30
|
+
yini --version
|
|
31
|
+
```
|
|
32
|
+
Should print the version (e.g., 1.0.0).
|
|
33
|
+
|
|
34
|
+
Then you may try:
|
|
35
|
+
```bash
|
|
36
|
+
yini --help
|
|
37
|
+
```
|
|
38
|
+
Should show you the CLI help for YINI.
|
|
39
|
+
|
|
40
|
+
3. **Test functionality**
|
|
41
|
+
Create a simple test file, for example: `config.yini`:
|
|
42
|
+
```yini
|
|
43
|
+
^ App
|
|
44
|
+
name = "My App Title"
|
|
45
|
+
version = "1.2.3"
|
|
46
|
+
pageSize = 25
|
|
47
|
+
darkTheme = off
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then run:
|
|
51
|
+
```bash
|
|
52
|
+
yini parse config.yini
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Expected result, your CLI should output a parsed version of the config and output something similar to:
|
|
56
|
+
```js
|
|
57
|
+
{
|
|
58
|
+
App: {
|
|
59
|
+
name: 'My App Title',
|
|
60
|
+
version: '1.2.3',
|
|
61
|
+
pageSize: 25,
|
|
62
|
+
darkTheme: false
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
⭐ If this was useful, [star it on GitHub](https://github.com/YINI-lang/yini-cli) — it helps a lot, thank you!
|
|
17
68
|
|
|
18
69
|
---
|
|
19
70
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
- Designed for compatibility with both **manual editing** and **automation**.
|
|
26
|
-
- 👉 See [how YINI differs from JSON, YAML, INI, and TOML](https://github.com/YINI-lang/yini-parser-typescript/tree/main/examples/compare-formats.md).
|
|
27
|
-
- Want the full syntax reference? See the [YINI Specification](https://github.com/YINI-lang/YINI-spec).
|
|
71
|
+
### Typical use cases
|
|
72
|
+
|
|
73
|
+
- Validating configuration files during development or CI.
|
|
74
|
+
- Inspecting and debugging structured configuration.
|
|
75
|
+
- Converting YINI files to JSON for tooling and automation.
|
|
28
76
|
|
|
29
77
|
---
|
|
30
78
|
|
|
31
|
-
##
|
|
79
|
+
## 🙋♀️ Why YINI?
|
|
80
|
+
- **Indentation-independent structure:** The YINI config format is indentation-independent, meaning any space or tab never changes meaning.
|
|
81
|
+
- **Explicit nesting:** It uses clear header markers (`^`, `^^`, `^^^`) to define hierarchy (like in Markdown), without long dotted keys.
|
|
82
|
+
- **Multiple data types:** Supports boolean literals (`true` / `false`, `Yes` / `No`, etc), numbers, arrays (lists), and JS-style objects natively, with explicit string syntax.
|
|
83
|
+
- **Comments support:** YINI supports multiple comment styles (`#`, `//`, `/* ... */`, and `;`) allowing one to document config directly in the file.
|
|
84
|
+
- **Predictable parsing rules:** Well-defined rules with optional strict and lenient modes, for different use-requirements.
|
|
85
|
+
|
|
86
|
+
---
|
|
32
87
|
|
|
33
|
-
|
|
34
|
-
```yini
|
|
35
|
-
// This is a comment in YINI
|
|
36
|
-
// YINI is a simple, human-readable configuration file format.
|
|
88
|
+
## Usage of command `yini`
|
|
37
89
|
|
|
38
|
-
|
|
39
|
-
|
|
90
|
+
```bash
|
|
91
|
+
Usage: yini [options] [command]
|
|
40
92
|
|
|
41
|
-
|
|
93
|
+
CLI for parsing and validating YINI config files.
|
|
42
94
|
|
|
43
|
-
|
|
44
|
-
|
|
95
|
+
Options:
|
|
96
|
+
-v, --version Output the version number.
|
|
97
|
+
-i, --info Show extended information (details, links, etc.).
|
|
98
|
+
-s, --strict Enable strict parsing mode.
|
|
99
|
+
-f, --force Continue parsing even if errors occur.
|
|
100
|
+
-q, --quiet Reduce output (show only errors).
|
|
101
|
+
--silent Suppress all output (even errors, exit code only).
|
|
102
|
+
-h, --help Display help for command.
|
|
45
103
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
104
|
+
Commands:
|
|
105
|
+
parse [options] <file> Parse a YINI file (*.yini) and print the result.
|
|
106
|
+
validate [options] <file> Checks if the file can be parsed as valid YINI.
|
|
107
|
+
info Deprecated: Use `yini --info` or `yini -i` instead.
|
|
108
|
+
help [command] Display help for command.
|
|
109
|
+
|
|
110
|
+
Examples:
|
|
111
|
+
$ yini parse config.yini
|
|
112
|
+
$ yini validate --strict config.yini
|
|
113
|
+
$ yini parse config.yini --pretty --output out.json
|
|
114
|
+
|
|
115
|
+
For help with a specific command, use -h or --help. For example:
|
|
116
|
+
$ yini validate --help
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Quick Look at YINI
|
|
122
|
+
|
|
123
|
+
Here's a small example showing YINI structure and comments:
|
|
124
|
+
```yini
|
|
125
|
+
// This is a comment in YINI
|
|
50
126
|
|
|
51
|
-
^ App //
|
|
127
|
+
^ App // Defines section (group) "App"
|
|
52
128
|
name = 'My Title' // Keys and values are written as key = value
|
|
53
129
|
items = 25
|
|
54
130
|
darkMode = true // "ON" and "YES" works too
|
|
@@ -57,6 +133,8 @@ YINI code looks like this:
|
|
|
57
133
|
^^ Special
|
|
58
134
|
primaryColor = #336699 // Hex number format
|
|
59
135
|
isCaching = false // "OFF" and "NO" works too
|
|
136
|
+
|
|
137
|
+
# This is a comment too.
|
|
60
138
|
```
|
|
61
139
|
|
|
62
140
|
**The above YINI converted to a JS object:**
|
|
@@ -96,65 +174,6 @@ That's it!
|
|
|
96
174
|
|
|
97
175
|
---
|
|
98
176
|
|
|
99
|
-
## Bigger Intro into YINI Config Format
|
|
100
|
-
**YINI** is a simple and readable configuration format. Sections are defined with `^ SectionName`, and values are assigned using `key = value`. The format supports common data types (same as those found in JSON), including strings, numbers, booleans, nulls, and lists.
|
|
101
|
-
|
|
102
|
-
To learn more, see the [Getting Started: Intro to YINI Config Format](https://github.com/YINI-lang/YINI-spec/blob/develop/Docs/Intro-to-YINI-Config-Format.md) tutorial.
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## Usage
|
|
107
|
-
|
|
108
|
-
### Installation
|
|
109
|
-
|
|
110
|
-
1. **Install it globally from npm — (requires Node.js)**
|
|
111
|
-
Open your terminal and run:
|
|
112
|
-
```
|
|
113
|
-
npm install -g yini-cli
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
2. **Verify installation**
|
|
117
|
-
Run this in your terminal:
|
|
118
|
-
```bash
|
|
119
|
-
yini --version
|
|
120
|
-
```
|
|
121
|
-
Should print the version (e.g., 1.0.0).
|
|
122
|
-
|
|
123
|
-
Then you may try:
|
|
124
|
-
```bash
|
|
125
|
-
yini --help
|
|
126
|
-
```
|
|
127
|
-
Should show you the CLI help for YINI.
|
|
128
|
-
|
|
129
|
-
3. **Test functionality**
|
|
130
|
-
Create a simple test file, for example: `config.yini`:
|
|
131
|
-
```yini
|
|
132
|
-
^ App
|
|
133
|
-
name = "My App Title"
|
|
134
|
-
version = "1.2.3"
|
|
135
|
-
pageSize = 25
|
|
136
|
-
darkTheme = off
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
Then run:
|
|
140
|
-
```bash
|
|
141
|
-
yini parse config.yini
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
Expected result, your CLI should output a parsed version of the config and output something similar to:
|
|
145
|
-
```js
|
|
146
|
-
{
|
|
147
|
-
App: {
|
|
148
|
-
name: 'My App Title',
|
|
149
|
-
version: '1.2.3',
|
|
150
|
-
pageSize: 25,
|
|
151
|
-
darkTheme: false
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
177
|
## 📤 Output Modes for `yini parse`
|
|
159
178
|
|
|
160
179
|
The `parse` command supports multiple output styles:
|
|
@@ -171,27 +190,29 @@ The `parse` command supports multiple output styles:
|
|
|
171
190
|
|
|
172
191
|
---
|
|
173
192
|
|
|
174
|
-
##
|
|
175
|
-
|
|
176
|
-
|
|
193
|
+
## 🛠 Roadmap
|
|
194
|
+
Areas of planned and possible future expansion:
|
|
195
|
+
|
|
196
|
+
1. **Improve existing commands** — Continued functionality improvements, better diagnostics, and expanded QA for `parse` and `validate` and their options.
|
|
197
|
+
2. Command `format`: Pretty-print or normalize a `.yini` file.
|
|
198
|
+
3. Command `lint`: Stricter stylistic checks (like `validate`, but opinionated).
|
|
199
|
+
4. Command `diff`: Compare two YINI files and show structural/config differences.
|
|
200
|
+
5. Command `convert`: Convert a `JSON` or `XML` file into YINI format.
|
|
201
|
+
|
|
202
|
+
---
|
|
177
203
|
|
|
204
|
+
## Links
|
|
178
205
|
- ➡️ [YINI Parser on npm](https://www.npmjs.com/package/yini-parser)
|
|
179
206
|
*Install and view package details.*
|
|
180
207
|
|
|
181
|
-
- ➡️ [
|
|
182
|
-
*
|
|
183
|
-
|
|
184
|
-
- ➡️ [YINI Parser on GitHub](https://github.com/YINI-lang/yini-parser-typescript)
|
|
185
|
-
*TypeScript source code, issue tracker, and contributing guide.*
|
|
208
|
+
- ➡️ [YINI Project](https://github.com/YINI-lang)
|
|
209
|
+
*YINI home on gitHub.*
|
|
186
210
|
|
|
187
|
-
|
|
188
|
-
*How does YINI differ: comparison with INI, YAML, and JSON.*
|
|
189
|
-
|
|
190
|
-
- ➡️ [Why YINI? (Project Rationale)](https://github.com/YINI-lang/YINI-spec/blob/release/RATIONALE.md)
|
|
191
|
-
*Learn about the motivations and design decisions behind YINI.*
|
|
211
|
+
---
|
|
192
212
|
|
|
193
|
-
|
|
194
|
-
|
|
213
|
+
## Contribution & Involvement
|
|
214
|
+
Interested in contributing or trying ideas?
|
|
215
|
+
Issues, feedback, and experiments are welcome — even small ones.
|
|
195
216
|
|
|
196
217
|
---
|
|
197
218
|
|
|
@@ -202,4 +223,9 @@ In this project on GitHub, the `libs` directory contains third party software an
|
|
|
202
223
|
|
|
203
224
|
---
|
|
204
225
|
|
|
205
|
-
|
|
226
|
+
If you found this useful, a GitHub star helps the project a lot ⭐
|
|
227
|
+
|
|
228
|
+
**^YINI ≡**
|
|
229
|
+
> YINI — Clear, Structured Configuration Files.
|
|
230
|
+
|
|
231
|
+
[yini-lang.org](https://yini-lang.org) · [YINI on GitHub](https://github.com/YINI-lang)
|
|
@@ -3,44 +3,45 @@ import path from 'node:path';
|
|
|
3
3
|
import util from 'util';
|
|
4
4
|
import YINI from 'yini-parser';
|
|
5
5
|
import { debugPrint, printObject, toPrettyJSON } from '../utils/print.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
6
|
+
// -------------------------------------------------------------------------
|
|
7
|
+
export const parseFile = (file, commandOptions) => {
|
|
8
|
+
const outputFile = commandOptions.output || '';
|
|
9
|
+
const isStrictMode = !!commandOptions.strict;
|
|
9
10
|
let outputStyle = 'JS-style';
|
|
10
11
|
debugPrint('file = ' + file);
|
|
11
|
-
debugPrint('output = ' +
|
|
12
|
-
debugPrint('
|
|
13
|
-
printObject(
|
|
14
|
-
if (
|
|
12
|
+
debugPrint('output = ' + commandOptions.output);
|
|
13
|
+
debugPrint('commandOptions:');
|
|
14
|
+
printObject(commandOptions);
|
|
15
|
+
if (commandOptions.pretty) {
|
|
15
16
|
outputStyle = 'Pretty-JSON';
|
|
16
17
|
}
|
|
17
|
-
else if (
|
|
18
|
-
outputStyle = 'Console.log';
|
|
19
|
-
}
|
|
20
|
-
else if (options.json) {
|
|
18
|
+
else if (commandOptions.json) {
|
|
21
19
|
outputStyle = 'JSON-compact';
|
|
22
20
|
}
|
|
23
21
|
else {
|
|
24
22
|
outputStyle = 'JS-style';
|
|
25
23
|
}
|
|
26
|
-
doParseFile(file,
|
|
24
|
+
doParseFile(file, commandOptions, outputStyle, outputFile);
|
|
27
25
|
};
|
|
28
|
-
const doParseFile = (file,
|
|
29
|
-
// let strictMode = !!
|
|
30
|
-
let
|
|
26
|
+
const doParseFile = (file, commandOptions, outputStyle, outputFile = '') => {
|
|
27
|
+
// let strictMode = !!commandOptions.strict
|
|
28
|
+
let preferredFailLevel = 'auto';
|
|
31
29
|
let includeMetaData = false;
|
|
32
30
|
debugPrint('File = ' + file);
|
|
33
31
|
debugPrint('outputStyle = ' + outputStyle);
|
|
32
|
+
const parseOptions = {
|
|
33
|
+
strictMode: commandOptions.strict ?? false,
|
|
34
|
+
// failLevel: 'errors',
|
|
35
|
+
failLevel: preferredFailLevel,
|
|
36
|
+
// failLevel: 'ignore-errors',
|
|
37
|
+
includeMetadata: includeMetaData,
|
|
38
|
+
};
|
|
39
|
+
// If --force then override fail-level.
|
|
40
|
+
if (commandOptions.force) {
|
|
41
|
+
parseOptions.failLevel = 'ignore-errors';
|
|
42
|
+
}
|
|
34
43
|
try {
|
|
35
|
-
|
|
36
|
-
// const parsed = YINI.parseFile(
|
|
37
|
-
//const parsed = YINI.parseFile(file)
|
|
38
|
-
const parsed = YINI.parseFile(file, isStrictMode, bailSensitivity, includeMetaData);
|
|
39
|
-
// const parsed = YINI.parse(raw)
|
|
40
|
-
// const output = options.pretty
|
|
41
|
-
// ? // ? JSON.stringify(parsed, null, 2)
|
|
42
|
-
// toPrettyJSON(parsed)
|
|
43
|
-
// : JSON.stringify(parsed)
|
|
44
|
+
const parsed = YINI.parseFile(file, parseOptions);
|
|
44
45
|
let output = '';
|
|
45
46
|
switch (outputStyle) {
|
|
46
47
|
case 'Pretty-JSON':
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { exit } from 'node:process';
|
|
3
|
+
import YINI from 'yini-parser';
|
|
4
|
+
const IS_DEBUG = false; // For local debugging purposes, etc.
|
|
5
|
+
// -------------------------------------------------------------------------
|
|
6
|
+
export const validateFile = (file, commandOptions = {}) => {
|
|
7
|
+
let parsedResult = undefined;
|
|
8
|
+
let isCatchedError = true;
|
|
9
|
+
const parseOptions = {
|
|
10
|
+
strictMode: commandOptions.strict ?? false,
|
|
11
|
+
// failLevel: 'errors',
|
|
12
|
+
failLevel: commandOptions.force ? 'ignore-errors' : 'errors',
|
|
13
|
+
// failLevel: 'ignore-errors',
|
|
14
|
+
includeMetadata: true,
|
|
15
|
+
includeDiagnostics: true,
|
|
16
|
+
silent: true,
|
|
17
|
+
};
|
|
18
|
+
try {
|
|
19
|
+
parsedResult = YINI.parseFile(file, parseOptions);
|
|
20
|
+
isCatchedError = false;
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
isCatchedError = true;
|
|
24
|
+
}
|
|
25
|
+
let metadata = null;
|
|
26
|
+
let errors = 0;
|
|
27
|
+
let warnings = 0;
|
|
28
|
+
let notices = 0;
|
|
29
|
+
let infos = 0;
|
|
30
|
+
if (!isCatchedError && parsedResult?.meta) {
|
|
31
|
+
metadata = parsedResult?.meta;
|
|
32
|
+
assert(metadata); // Make sure there is metadata!
|
|
33
|
+
// printObject(metadata, true)
|
|
34
|
+
assert(metadata.diagnostics);
|
|
35
|
+
const diag = metadata.diagnostics;
|
|
36
|
+
errors = diag.errors.errorCount;
|
|
37
|
+
warnings = diag.warnings.warningCount;
|
|
38
|
+
notices = diag.notices.noticeCount;
|
|
39
|
+
infos = diag.infos.infoCount;
|
|
40
|
+
}
|
|
41
|
+
IS_DEBUG && console.log();
|
|
42
|
+
IS_DEBUG && console.log('isCatchedError = ' + isCatchedError);
|
|
43
|
+
IS_DEBUG && console.log('TEMP OUTPUT');
|
|
44
|
+
IS_DEBUG && console.log('isCatchedError = ' + isCatchedError);
|
|
45
|
+
IS_DEBUG && console.log(' errors = ' + errors);
|
|
46
|
+
IS_DEBUG && console.log('warnings = ' + warnings);
|
|
47
|
+
IS_DEBUG && console.log(' notices = ' + notices);
|
|
48
|
+
IS_DEBUG && console.log(' infor = ' + infos);
|
|
49
|
+
IS_DEBUG && console.log('metadata = ' + metadata);
|
|
50
|
+
IS_DEBUG &&
|
|
51
|
+
console.log('includeMetadata = ' +
|
|
52
|
+
metadata?.diagnostics?.effectiveOptions.includeMetadata);
|
|
53
|
+
IS_DEBUG && console.log('commandOptions.report = ' + commandOptions?.report);
|
|
54
|
+
IS_DEBUG && console.log();
|
|
55
|
+
if (!commandOptions.silent && !isCatchedError) {
|
|
56
|
+
if (commandOptions.report) {
|
|
57
|
+
if (!metadata) {
|
|
58
|
+
console.error('Internal Error: No meta data found');
|
|
59
|
+
}
|
|
60
|
+
assert(metadata); // Make sure there is metadata!
|
|
61
|
+
console.log();
|
|
62
|
+
console.log(formatToReport(file, metadata).trim());
|
|
63
|
+
}
|
|
64
|
+
if (commandOptions.details) {
|
|
65
|
+
if (!metadata) {
|
|
66
|
+
console.error('Internal Error: No meta data found');
|
|
67
|
+
}
|
|
68
|
+
assert(metadata); // Make sure there is metadata!
|
|
69
|
+
console.log();
|
|
70
|
+
printDetailsOnAllIssue(file, metadata);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//state returned:
|
|
74
|
+
// - passed (no errors/warnings),
|
|
75
|
+
// - finished (with warnings, no errors) / or - passed with warnings
|
|
76
|
+
// - failed (errors),
|
|
77
|
+
if (isCatchedError) {
|
|
78
|
+
errors = 1;
|
|
79
|
+
}
|
|
80
|
+
// console.log()
|
|
81
|
+
if (errors) {
|
|
82
|
+
// red ✖
|
|
83
|
+
console.error(formatToStatus('Failed', errors, warnings, notices, infos));
|
|
84
|
+
exit(1);
|
|
85
|
+
}
|
|
86
|
+
else if (warnings) {
|
|
87
|
+
// yellow ⚠️
|
|
88
|
+
console.warn(formatToStatus('Passed-with-Warnings', errors, warnings, notices, infos));
|
|
89
|
+
exit(0);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// green ✔
|
|
93
|
+
console.log(formatToStatus('Passed', errors, warnings, notices, infos));
|
|
94
|
+
exit(0);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const formatToStatus = (statusType, errors, warnings, notices, infos) => {
|
|
98
|
+
const totalMsgs = errors + warnings + notices + infos;
|
|
99
|
+
let str = ``;
|
|
100
|
+
switch (statusType) {
|
|
101
|
+
case 'Passed':
|
|
102
|
+
str = '✔ Validation passed';
|
|
103
|
+
break;
|
|
104
|
+
case 'Passed-with-Warnings':
|
|
105
|
+
str = '⚠️ Validation finished';
|
|
106
|
+
break;
|
|
107
|
+
case 'Failed':
|
|
108
|
+
str = '✖ Validation failed';
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
str += ` (${errors} errors, ${warnings} warnings, ${totalMsgs} total messages)`;
|
|
112
|
+
return str;
|
|
113
|
+
};
|
|
114
|
+
// --- Format to a Report --------------------------------------------------------
|
|
115
|
+
//@todo format parsed.meta to report as
|
|
116
|
+
/*
|
|
117
|
+
- Produce a summary-level validation report.
|
|
118
|
+
- Output is structured and concise (e.g. JSON or table-like).
|
|
119
|
+
- Focus on counts, pass/fail, severity summary.
|
|
120
|
+
|
|
121
|
+
Example:
|
|
122
|
+
Validation report for config.yini:
|
|
123
|
+
Errors: 3
|
|
124
|
+
Warnings: 1
|
|
125
|
+
Notices: 0
|
|
126
|
+
Result: INVALID
|
|
127
|
+
*/
|
|
128
|
+
const formatToReport = (fileWithPath, metadata) => {
|
|
129
|
+
// console.log('formatToReport(..)')
|
|
130
|
+
// printObject(metadata)
|
|
131
|
+
// console.log()
|
|
132
|
+
assert(metadata.diagnostics);
|
|
133
|
+
const diag = metadata.diagnostics;
|
|
134
|
+
const issuesCount = diag.errors.errorCount +
|
|
135
|
+
diag.warnings.warningCount +
|
|
136
|
+
diag.notices.noticeCount +
|
|
137
|
+
diag.infos.infoCount;
|
|
138
|
+
const str = `Validation Report
|
|
139
|
+
=================
|
|
140
|
+
|
|
141
|
+
File "${fileWithPath}"
|
|
142
|
+
Issues: ${issuesCount}
|
|
143
|
+
|
|
144
|
+
Summary
|
|
145
|
+
-------
|
|
146
|
+
Mode: ${metadata.mode}
|
|
147
|
+
Strict: ${metadata.mode === 'strict'}
|
|
148
|
+
|
|
149
|
+
Errors: ${diag.errors.errorCount}
|
|
150
|
+
Warnings: ${diag.warnings.warningCount}
|
|
151
|
+
Notices: ${diag.notices.noticeCount}
|
|
152
|
+
Infos: ${diag.infos.infoCount}
|
|
153
|
+
|
|
154
|
+
Stats
|
|
155
|
+
-----
|
|
156
|
+
Line Count: ${metadata.source.lineCount}
|
|
157
|
+
Section Count: ${metadata.structure.sectionCount}
|
|
158
|
+
Member Count: ${metadata.structure.memberCount}
|
|
159
|
+
Nesting Depth: ${metadata.structure.maxDepth}
|
|
160
|
+
|
|
161
|
+
Has @YINI: ${metadata.source.hasYiniMarker}
|
|
162
|
+
Has /END: ${metadata.source.hasDocumentTerminator}
|
|
163
|
+
Byte Size: ${metadata.source.sourceType === 'inline' ? 'n/a' : metadata.source.byteSize + ' bytes'}
|
|
164
|
+
`;
|
|
165
|
+
return str;
|
|
166
|
+
};
|
|
167
|
+
// -------------------------------------------------------------------------
|
|
168
|
+
// --- Format to a Details --------------------------------------------------------
|
|
169
|
+
//@todo format parsed.meta to details as
|
|
170
|
+
/*
|
|
171
|
+
- Show full detailed validation messages.
|
|
172
|
+
- Output includes line numbers, columns, error codes, and descriptive text.
|
|
173
|
+
- Useful for debugging YINI files.
|
|
174
|
+
|
|
175
|
+
Example:
|
|
176
|
+
Error at line 5, column 9: Unexpected '/END' — expected <EOF>
|
|
177
|
+
Warning at line 10, column 3: Section level skipped (0 → 2)
|
|
178
|
+
Notice at line 1: Unused @yini directive
|
|
179
|
+
*/
|
|
180
|
+
const printDetailsOnAllIssue = (fileWithPath, metadata) => {
|
|
181
|
+
// console.log('printDetails(..)')
|
|
182
|
+
// printObject(metadata)
|
|
183
|
+
// console.log(toPrettyJSON(metadata))
|
|
184
|
+
// console.log()
|
|
185
|
+
assert(metadata.diagnostics);
|
|
186
|
+
const diag = metadata.diagnostics;
|
|
187
|
+
console.log('Details');
|
|
188
|
+
console.log('-------');
|
|
189
|
+
console.log();
|
|
190
|
+
const errors = diag.errors.payload;
|
|
191
|
+
printIssues('Error ', 'E', errors);
|
|
192
|
+
const warnings = diag.warnings.payload;
|
|
193
|
+
printIssues('Warning', 'W', warnings);
|
|
194
|
+
const notices = diag.notices.payload;
|
|
195
|
+
printIssues('Notice ', 'N', notices);
|
|
196
|
+
const infos = diag.infos.payload;
|
|
197
|
+
printIssues('Info ', 'I', infos);
|
|
198
|
+
return;
|
|
199
|
+
};
|
|
200
|
+
// -------------------------------------------------------------------------
|
|
201
|
+
const printIssues = (typeLabel, prefix, issues) => {
|
|
202
|
+
const leftPadding = ' ';
|
|
203
|
+
issues.forEach((iss, i) => {
|
|
204
|
+
const id = '#' + prefix + '-0' + (i + 1);
|
|
205
|
+
// const id: string = '' + prefix + '-0' + (i+1) + ':'
|
|
206
|
+
let str = `${typeLabel} [${id}]:\n`;
|
|
207
|
+
str +=
|
|
208
|
+
leftPadding +
|
|
209
|
+
`At line ${iss.line}, column ${iss.column}: ${iss.message}`;
|
|
210
|
+
if (iss.advice)
|
|
211
|
+
str += '\n' + leftPadding + iss.advice;
|
|
212
|
+
if (iss.hint)
|
|
213
|
+
str += '\n' + leftPadding + iss.hint;
|
|
214
|
+
console.log(str);
|
|
215
|
+
console.log();
|
|
216
|
+
});
|
|
217
|
+
};
|
package/dist/descriptions.js
CHANGED
|
@@ -2,5 +2,5 @@ export const descriptions = {
|
|
|
2
2
|
yini: 'CLI for parsing and validating YINI config files.',
|
|
3
3
|
'For-command-parse': 'Parse a YINI file (*.yini) and print the result.',
|
|
4
4
|
'For-command-validate': 'Checks if the file can be parsed as valid YINI.',
|
|
5
|
-
'For-command-info': '
|
|
5
|
+
'For-command-info': 'Deprecated: Use `yini --info` or `yini -i` instead.',
|
|
6
6
|
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The main (global) option help.
|
|
3
|
+
*/
|
|
4
|
+
import { getPackageName, getPackageVersion } from '../utils/yiniCliHelpers.js';
|
|
5
|
+
/**
|
|
6
|
+
* Text to be displayed BEFORE the built-in help text block.
|
|
7
|
+
*/
|
|
8
|
+
export const getHelpTextBefore = () => {
|
|
9
|
+
return `${getPackageName()} ${getPackageVersion()}
|
|
10
|
+
YINI CLI (Yet another INI)
|
|
11
|
+
|
|
12
|
+
Parse and validate YINI configuration files.
|
|
13
|
+
A config format, inspired by INI, with type-safe values, nested
|
|
14
|
+
sections, comments, minimal syntax noise, and optional strict mode.
|
|
15
|
+
|
|
16
|
+
Designed for clarity and consistency. :)\n`;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Text to be displayed AFTER the built-in help text block.
|
|
20
|
+
*/
|
|
21
|
+
export const getHelpTextAfter = () => {
|
|
22
|
+
return `
|
|
23
|
+
Examples:
|
|
24
|
+
$ yini parse config.yini
|
|
25
|
+
$ yini validate --strict config.yini
|
|
26
|
+
$ yini parse config.yini --pretty --output out.json
|
|
27
|
+
|
|
28
|
+
For help with a specific command, use -h or --help. For example:
|
|
29
|
+
$ yini validate --help
|
|
30
|
+
|
|
31
|
+
Sample "config.yini":
|
|
32
|
+
^ App
|
|
33
|
+
title = 'My App'
|
|
34
|
+
items = 10
|
|
35
|
+
debug = ON
|
|
36
|
+
|
|
37
|
+
^ Server
|
|
38
|
+
host = 'localhost'
|
|
39
|
+
port = 8080
|
|
40
|
+
useTLS = OFF
|
|
41
|
+
|
|
42
|
+
// Sub-section of Server.
|
|
43
|
+
^^ Login
|
|
44
|
+
username = 'user'
|
|
45
|
+
password = 'secret'
|
|
46
|
+
|
|
47
|
+
More info:
|
|
48
|
+
https://github.com/YINI-lang/yini-cli
|
|
49
|
+
|
|
50
|
+
Into to YINI Config:
|
|
51
|
+
https://github.com/YINI-lang/YINI-spec/blob/develop/Docs/Intro-to-YINI-Config-Format.md`;
|
|
52
|
+
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createRequire } from 'module';
|
|
2
|
+
// import pkg from '../../package.json' with { type: 'json' } // NOTE: Must use { type: 'json' } when in ESM mode.
|
|
2
3
|
const require = createRequire(import.meta.url);
|
|
3
4
|
const pkg = require('../../package.json');
|
|
4
5
|
export const printInfo = () => {
|
|
5
|
-
console.log(
|
|
6
|
+
console.log(`*** YINI CLI ***`);
|
|
6
7
|
console.log(`yini-cli: ${pkg.version}`);
|
|
7
8
|
console.log(`yini-parser: ${pkg.dependencies['yini-parser'].replace('^', '')}`);
|
|
8
9
|
console.log(`Author: ${pkg.author}`);
|
package/dist/index.js
CHANGED
|
@@ -1,187 +1,158 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
//
|
|
2
|
+
//
|
|
3
|
+
// (!) IMPORTANT: Leave the top shebang as the very first line! (otherwise command will break)
|
|
4
|
+
//
|
|
4
5
|
import { createRequire } from 'module';
|
|
5
6
|
import { Command } from 'commander';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { validateFile } from './commands/validate.js';
|
|
7
|
+
import { parseFile } from './commands/parseCommand.js';
|
|
8
|
+
import { validateFile, } from './commands/validateCommand.js';
|
|
9
9
|
import { isDebug, isDev } from './config/env.js';
|
|
10
10
|
import { descriptions as descr } from './descriptions.js';
|
|
11
|
+
import { getHelpTextAfter, getHelpTextBefore, } from './globalOptions/helpOption.js';
|
|
12
|
+
import { printInfo } from './globalOptions/infoOption.js';
|
|
11
13
|
import { debugPrint, toPrettyJSON } from './utils/print.js';
|
|
14
|
+
import { getPackageVersion } from './utils/yiniCliHelpers.js';
|
|
12
15
|
const require = createRequire(import.meta.url);
|
|
13
|
-
const pkg = require('../package.json')
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
Stringigies JSON using using JSON.stringify(obj) (compact, machine-parseable)
|
|
47
|
-
to stdout
|
|
48
|
-
* yini parse config.yini --output out.txt
|
|
49
|
-
JS-style object
|
|
50
|
-
to out.txt
|
|
51
|
-
* yini parse config.yini --pretty --output out.json
|
|
52
|
-
Pretty JSON
|
|
53
|
-
to out.json
|
|
54
|
-
|
|
55
|
-
*/
|
|
56
|
-
// Display help for command
|
|
57
|
-
program
|
|
16
|
+
// const pkg = require('../package.json')
|
|
17
|
+
// --- Helper functions --------------------------------------------------------
|
|
18
|
+
function appendGlobalOptionsTo(cmd) {
|
|
19
|
+
const help = program.createHelp();
|
|
20
|
+
const globals = help.visibleOptions(program);
|
|
21
|
+
if (!globals.length)
|
|
22
|
+
return;
|
|
23
|
+
const lines = globals
|
|
24
|
+
// .map(
|
|
25
|
+
// (opt) =>
|
|
26
|
+
// ` ${help.optionTerm(opt)} ${help.optionDescription(opt)}`,
|
|
27
|
+
// )
|
|
28
|
+
// .join('\n')
|
|
29
|
+
.map((option) => {
|
|
30
|
+
const optName = option.name();
|
|
31
|
+
if (optName === 'version' ||
|
|
32
|
+
optName === 'info' ||
|
|
33
|
+
optName === 'help') {
|
|
34
|
+
debugPrint('Skip patching option.name() = ' +
|
|
35
|
+
option.name() +
|
|
36
|
+
' into per-command help');
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
return ` ${help.optionTerm(option)} ${help.optionDescription(option)}`;
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
.join('\n')
|
|
43
|
+
.trim();
|
|
44
|
+
cmd.addHelpText('after', `\nGlobal options:\n ${lines}`);
|
|
45
|
+
// cmd.addHelpText('after', ` ${lines}`)
|
|
46
|
+
}
|
|
47
|
+
// -------------------------------------------------------------------------
|
|
48
|
+
const program = new Command()
|
|
58
49
|
.name('yini')
|
|
59
50
|
.description(descr.yini)
|
|
60
51
|
// Below will replace all auto-registered items (especially the descriptions starting with a capital and ending with a period).
|
|
61
|
-
.version(
|
|
52
|
+
.version(getPackageVersion(), '-v, --version', 'Output the version number.')
|
|
62
53
|
.helpOption('-h, --help', 'Display help for command.')
|
|
63
54
|
.helpCommand('help [command]', 'Display help for command.');
|
|
64
|
-
program.addHelpText('before',
|
|
65
|
-
|
|
66
|
-
For parsing and validating YINI configuration files.
|
|
67
|
-
A config format, inspired by INI, with type-safe values, nested
|
|
68
|
-
sections, comments, minimal syntax noise, and optional strict mode.
|
|
69
|
-
|
|
70
|
-
Designed for clarity and consistency. :)\n`);
|
|
71
|
-
program.addHelpText('after', `
|
|
72
|
-
Examples:
|
|
73
|
-
$ yini parse config.yini
|
|
74
|
-
$ yini validate config.yini --strict
|
|
75
|
-
$ yini parse config.yini --pretty --output out.json
|
|
76
|
-
|
|
77
|
-
Example of "config.yini":
|
|
78
|
-
^ App
|
|
79
|
-
title = 'My App'
|
|
80
|
-
items = 10
|
|
81
|
-
debug = ON
|
|
82
|
-
|
|
83
|
-
^ Server
|
|
84
|
-
host = 'localhost'
|
|
85
|
-
port = 8080
|
|
86
|
-
useTLS = OFF
|
|
87
|
-
|
|
88
|
-
// Sub-section of Server.
|
|
89
|
-
^^ Login
|
|
90
|
-
username = 'user'
|
|
91
|
-
password = 'secret'
|
|
92
|
-
|
|
93
|
-
More info:
|
|
94
|
-
https://github.com/YINI-lang/yini-cli
|
|
95
|
-
|
|
96
|
-
Into to YINI Config:
|
|
97
|
-
https://github.com/YINI-lang/YINI-spec/blob/develop/Docs/Intro-to-YINI-Config-Format.md`);
|
|
98
|
-
//program.command('help [command]').description('Display help for command')
|
|
55
|
+
program.addHelpText('before', getHelpTextBefore());
|
|
56
|
+
program.addHelpText('after', getHelpTextAfter());
|
|
99
57
|
/**
|
|
100
|
-
*
|
|
101
|
-
|
|
58
|
+
* The (main/global) option: "--info, --strict, --quite, --silent"
|
|
59
|
+
*/
|
|
60
|
+
// Suggestions for future: --verbose, --debug, --no-color, --color, --timing, --stdin
|
|
61
|
+
program
|
|
62
|
+
.option('-i, --info', 'Show extended information (details, links, etc.).')
|
|
63
|
+
.option('-s, --strict', 'Enable strict parsing mode.')
|
|
64
|
+
.option('-f, --force', 'Continue parsing even if errors occur.')
|
|
65
|
+
.option('-q, --quiet', 'Reduce output (show only errors).')
|
|
66
|
+
.option('--silent', 'Suppress all output (even errors, exit code only).')
|
|
67
|
+
.action((options) => {
|
|
68
|
+
debugPrint('Run global options');
|
|
69
|
+
if (isDebug()) {
|
|
70
|
+
console.log('Global options:');
|
|
71
|
+
console.log(toPrettyJSON(options));
|
|
72
|
+
}
|
|
73
|
+
printInfo();
|
|
74
|
+
});
|
|
75
|
+
/**
|
|
76
|
+
* The (main/global) option: "--info"
|
|
102
77
|
*/
|
|
103
78
|
// program
|
|
104
|
-
// .
|
|
105
|
-
// .
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
// .action((file, options) => {
|
|
111
|
-
// if (file) {
|
|
112
|
-
// parseFile(file, options)
|
|
113
|
-
// } else {
|
|
114
|
-
// program.help()
|
|
79
|
+
// .option('-s, --strict', 'Enable parsing in strict-mode.')
|
|
80
|
+
// .action((options) => {
|
|
81
|
+
// debugPrint('Run (global) option "strict"')
|
|
82
|
+
// if (isDebug()) {
|
|
83
|
+
// console.log('options:')
|
|
84
|
+
// console.log(toPrettyJSON(options))
|
|
115
85
|
// }
|
|
86
|
+
// printInfo()
|
|
116
87
|
// })
|
|
117
|
-
|
|
118
|
-
|
|
88
|
+
/**
|
|
89
|
+
* The command: "parse <file>"
|
|
90
|
+
*/
|
|
91
|
+
const parseCmd = program
|
|
119
92
|
.command('parse <file>')
|
|
120
93
|
.description(descr['For-command-parse'])
|
|
121
|
-
.option('--strict', 'Parse YINI in strict-mode.')
|
|
122
94
|
.option('--pretty', 'Pretty-print output as JSON.')
|
|
123
|
-
// .option('--log', 'Use console.log output format (compact, quick view)')
|
|
124
95
|
.option('--json', 'Compact JSON output using JSON.stringify.')
|
|
125
96
|
.option('--output <file>', 'Write output to a specified file.')
|
|
126
97
|
.action((file, options) => {
|
|
98
|
+
const globals = program.opts(); // Global options.
|
|
99
|
+
const mergedOptions = { ...globals, ...options }; // Merge global options with per-command options.
|
|
127
100
|
debugPrint('Run command "parse"');
|
|
128
101
|
debugPrint('isDebug(): ' + isDebug());
|
|
129
102
|
debugPrint('isDev() : ' + isDev());
|
|
130
103
|
debugPrint(`<file> = ${file}`);
|
|
131
104
|
if (isDebug()) {
|
|
132
|
-
console.log('
|
|
133
|
-
console.log(toPrettyJSON(
|
|
105
|
+
console.log('mergedOptions:');
|
|
106
|
+
console.log(toPrettyJSON(mergedOptions));
|
|
134
107
|
}
|
|
135
108
|
if (file) {
|
|
136
|
-
parseFile(file,
|
|
109
|
+
parseFile(file, mergedOptions);
|
|
137
110
|
}
|
|
138
111
|
else {
|
|
139
112
|
program.help();
|
|
140
113
|
}
|
|
141
114
|
});
|
|
115
|
+
appendGlobalOptionsTo(parseCmd);
|
|
142
116
|
/**
|
|
143
|
-
*
|
|
144
|
-
* yini validate config.yini
|
|
145
|
-
* yini validate config.yini --strict
|
|
146
|
-
* yini validate config.yini --details
|
|
147
|
-
* yini validate config.yini --silent
|
|
148
|
-
*
|
|
149
|
-
* If details:
|
|
150
|
-
* Details:
|
|
151
|
-
* - YINI version: 1.0.0-beta.6
|
|
152
|
-
* - Mode: strict
|
|
153
|
-
* - Keys: 42
|
|
154
|
-
* - Sections: 6
|
|
155
|
-
* - Nesting depth: 3
|
|
156
|
-
* - Has @yini: true
|
|
117
|
+
* The command: "validate <file>"
|
|
157
118
|
*/
|
|
158
|
-
program
|
|
119
|
+
const validateCmd = program
|
|
159
120
|
.command('validate <file>')
|
|
160
121
|
.description(descr['For-command-validate'])
|
|
161
|
-
.option('--
|
|
162
|
-
.option('--details', 'Print detailed
|
|
163
|
-
.option('--silent', 'Suppress output')
|
|
122
|
+
.option('--report', 'Print detailed meta-data info (e.g., key count, nesting, etc.).')
|
|
123
|
+
.option('--details', 'Print detailed validation info (e.g., line locations, error codes, descriptive text, etc.).')
|
|
124
|
+
// .option('--silent', 'Suppress output')
|
|
164
125
|
.action((file, options) => {
|
|
165
|
-
|
|
126
|
+
const globals = program.opts(); // Global options.
|
|
127
|
+
const mergedOptions = { ...globals, ...options }; // Merge global options with per-command options.
|
|
128
|
+
debugPrint('Run command "parse"');
|
|
129
|
+
debugPrint('isDebug(): ' + isDebug());
|
|
130
|
+
debugPrint('isDev() : ' + isDev());
|
|
131
|
+
debugPrint(`<file> = ${file}`);
|
|
132
|
+
if (isDebug()) {
|
|
133
|
+
console.log('mergedOptions:');
|
|
134
|
+
console.log(toPrettyJSON(mergedOptions));
|
|
135
|
+
}
|
|
166
136
|
if (file) {
|
|
167
|
-
validateFile(file,
|
|
137
|
+
validateFile(file, mergedOptions);
|
|
168
138
|
}
|
|
169
139
|
else {
|
|
170
140
|
program.help();
|
|
171
141
|
}
|
|
172
142
|
});
|
|
173
|
-
|
|
143
|
+
appendGlobalOptionsTo(validateCmd);
|
|
144
|
+
// About to get deleted, moved to main option --info
|
|
145
|
+
//@todo Delete
|
|
174
146
|
program
|
|
175
147
|
.command('info')
|
|
176
|
-
// .command('')
|
|
177
148
|
.description(descr['For-command-info'])
|
|
178
|
-
// .option('info')
|
|
179
149
|
.action((options) => {
|
|
180
150
|
debugPrint('Run command "info"');
|
|
181
151
|
if (isDebug()) {
|
|
182
152
|
console.log('options:');
|
|
183
153
|
console.log(toPrettyJSON(options));
|
|
184
154
|
}
|
|
155
|
+
console.warn('Deprecated: Use `yini --info` or `yini -i` instead of `yini info`.');
|
|
185
156
|
printInfo();
|
|
186
157
|
});
|
|
187
158
|
// NOTE: Converting YINI files to other formats than json and js.
|
package/dist/types.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
export interface
|
|
1
|
+
export interface IGlobalOptions {
|
|
2
2
|
strict?: boolean;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
output?: string;
|
|
3
|
+
force?: boolean;
|
|
4
|
+
quiet?: boolean;
|
|
5
|
+
silent?: boolean;
|
|
7
6
|
}
|
|
8
|
-
export type TBailSensitivity = 'auto' | 0 | 1 | 2;
|
package/dist/types.js
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
+
// //export type TBailSensitivity = 'auto' | 0 | 1 | 2
|
|
2
|
+
// export type TPreferredFailLevel = 'auto' | 0 | 1 | 2 // Preferred bail sensitivity level.
|
|
3
|
+
// export type TBailSensitivityLevel = 0 | 1 | 2 // Bail sensitivity level.
|
|
1
4
|
export {};
|
|
5
|
+
// -------------------------------------------------------------------------
|
package/dist/utils/print.d.ts
CHANGED
|
@@ -2,14 +2,13 @@ export declare const debugPrint: (str?: any) => void;
|
|
|
2
2
|
export declare const devPrint: (str?: any) => void;
|
|
3
3
|
export declare const toJSON: (obj: any) => string;
|
|
4
4
|
export declare const toPrettyJSON: (obj: any) => string;
|
|
5
|
-
/**
|
|
6
|
-
* Pretty-prints a JavaScript object as formatted JSON to the console.
|
|
5
|
+
/** Pretty-prints a JavaScript object as formatted JSON to the console.
|
|
7
6
|
* Strict JSON, all keys are enclosed in ", etc.
|
|
8
7
|
*/
|
|
9
|
-
export declare const printJSON: (obj: any) => void;
|
|
8
|
+
export declare const printJSON: (obj: any, isForce?: boolean) => void;
|
|
10
9
|
/**
|
|
11
10
|
* Print a full JavaScript object in a human-readable way (not as JSON).
|
|
12
11
|
* Not strict JSON, and shows functions, symbols, getters/setters, and class names.
|
|
13
12
|
* @param isColors If true, the output is styled with ANSI color codes.
|
|
14
13
|
*/
|
|
15
|
-
export declare const printObject: (obj: any, isColors?: boolean) => void;
|
|
14
|
+
export declare const printObject: (obj: any, isForce?: boolean, isColors?: boolean) => void;
|
package/dist/utils/print.js
CHANGED
|
@@ -18,13 +18,14 @@ export const toPrettyJSON = (obj) => {
|
|
|
18
18
|
const str = JSON.stringify(obj, null, 4);
|
|
19
19
|
return str;
|
|
20
20
|
};
|
|
21
|
-
/**
|
|
22
|
-
* Pretty-prints a JavaScript object as formatted JSON to the console.
|
|
21
|
+
/** Pretty-prints a JavaScript object as formatted JSON to the console.
|
|
23
22
|
* Strict JSON, all keys are enclosed in ", etc.
|
|
24
23
|
*/
|
|
25
|
-
export const printJSON = (obj) => {
|
|
26
|
-
if (
|
|
27
|
-
|
|
24
|
+
export const printJSON = (obj, isForce = false) => {
|
|
25
|
+
if (!isForce) {
|
|
26
|
+
if (isProdEnv() || (isTestEnv() && !isDebug()))
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
28
29
|
const str = toPrettyJSON(obj);
|
|
29
30
|
console.log(str);
|
|
30
31
|
};
|
|
@@ -33,8 +34,10 @@ export const printJSON = (obj) => {
|
|
|
33
34
|
* Not strict JSON, and shows functions, symbols, getters/setters, and class names.
|
|
34
35
|
* @param isColors If true, the output is styled with ANSI color codes.
|
|
35
36
|
*/
|
|
36
|
-
export const printObject = (obj, isColors = true) => {
|
|
37
|
-
if (
|
|
38
|
-
|
|
37
|
+
export const printObject = (obj, isForce = false, isColors = true) => {
|
|
38
|
+
if (!isForce) {
|
|
39
|
+
if (isProdEnv() || (isTestEnv() && !isDebug()))
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
39
42
|
console.log(util.inspect(obj, { depth: null, colors: isColors }));
|
|
40
43
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { dirname, resolve } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = dirname(__filename);
|
|
6
|
+
const pkg = JSON.parse(readFileSync(resolve(__dirname, '../../package.json'), 'utf-8'));
|
|
7
|
+
// import pkg from '../../package.json' with { type: 'json' } // NOTE: Must use { type: 'json' } when in ESM mode.
|
|
8
|
+
export const getPackageName = () => {
|
|
9
|
+
return '' + pkg.name;
|
|
10
|
+
};
|
|
11
|
+
export const getPackageVersion = () => {
|
|
12
|
+
return '' + pkg.version;
|
|
13
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yini-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1-beta",
|
|
4
4
|
"description": "CLI for parsing and validating YINI config files: type-safe values, nested sections, comments, minimal syntax noise, and optional strict mode.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"yini",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"bin": {
|
|
30
30
|
"yini": "./dist/index.js"
|
|
31
31
|
},
|
|
32
|
-
"homepage": "https://
|
|
32
|
+
"homepage": "https://yini-lang.org/",
|
|
33
33
|
"license": "Apache-2.0",
|
|
34
34
|
"files": [
|
|
35
35
|
"dist/",
|
|
@@ -46,15 +46,18 @@
|
|
|
46
46
|
"start": "node ./bin/yini.js",
|
|
47
47
|
"start:dev": "cross-env NODE_ENV=development isDev=1 tsx src/index.ts",
|
|
48
48
|
"start:dev:debug": "cross-env isDebug=1 npm run start:dev",
|
|
49
|
+
"run:help": "npm run start -- --help",
|
|
49
50
|
"run:version": "npm run start -- --version",
|
|
50
|
-
"run:info": "npm run start -- info",
|
|
51
|
+
"run:info": "npm run start -- --info",
|
|
51
52
|
"run:parse": "npm run start -- parse sample.yini",
|
|
53
|
+
"run:validate": "npm run start -- validate sample.yini",
|
|
52
54
|
"test:smoke": "vitest tests/smoke",
|
|
53
55
|
"test:general": "vitest tests/general",
|
|
54
56
|
"test": "vitest run",
|
|
55
57
|
"test:debug": "cross-env isDebug=1 vitest run --reporter=verbose",
|
|
56
58
|
"test:general:debug": "cross-env isDebug=1 npm run test:general",
|
|
57
59
|
"test:watch": "vitest --watch",
|
|
60
|
+
"test:cov": "nyc npm test",
|
|
58
61
|
"ci:test": "vitest run --reporter=verbose",
|
|
59
62
|
"ci:test:smoke": "vitest run tests/smoke --reporter=verbose",
|
|
60
63
|
"lint": "eslint src --ext .ts",
|
|
@@ -67,8 +70,8 @@
|
|
|
67
70
|
},
|
|
68
71
|
"author": "Marko K. Seppänen",
|
|
69
72
|
"dependencies": {
|
|
70
|
-
"commander": "^14.0.
|
|
71
|
-
"yini-parser": "^1.
|
|
73
|
+
"commander": "^14.0.1",
|
|
74
|
+
"yini-parser": "^1.3.2-beta"
|
|
72
75
|
},
|
|
73
76
|
"devDependencies": {
|
|
74
77
|
"@eslint/js": "^9.31.0",
|
|
@@ -83,6 +86,7 @@
|
|
|
83
86
|
"execa": "^9.6.0",
|
|
84
87
|
"husky": "^9.1.7",
|
|
85
88
|
"lint-staged": "^16.0.0",
|
|
89
|
+
"nyc": "^17.1.0",
|
|
86
90
|
"prettier": "^3.5.3",
|
|
87
91
|
"tsx": "^4.7.0",
|
|
88
92
|
"typescript": "^5.8.3",
|
package/sample.yini
CHANGED
package/dist/commands/parse.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { exit } from 'node:process';
|
|
3
|
-
import YINI from 'yini-parser';
|
|
4
|
-
import { printObject } from '../utils/print.js';
|
|
5
|
-
export const validateFile = (file, options = {}) => {
|
|
6
|
-
try {
|
|
7
|
-
const content = fs.readFileSync(file, 'utf-8');
|
|
8
|
-
const isMeta = true;
|
|
9
|
-
const parsed = YINI.parse(content, options.strict ?? false, 'auto', isMeta);
|
|
10
|
-
if (!options.silent) {
|
|
11
|
-
console.log(`✔ File is valid${options.strict ? ' (strict mode)' : ''}.`);
|
|
12
|
-
if (options.details) {
|
|
13
|
-
//@todo format parsed.meta to details as
|
|
14
|
-
/*
|
|
15
|
-
* Details:
|
|
16
|
-
* - YINI version: 1.0.0-beta.6
|
|
17
|
-
* - Mode: strict
|
|
18
|
-
* - Keys: 42
|
|
19
|
-
* - Sections: 6
|
|
20
|
-
* - Nesting depth: 3
|
|
21
|
-
* - Has @yini: true
|
|
22
|
-
*/
|
|
23
|
-
printObject(parsed.meta);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
exit(0);
|
|
27
|
-
}
|
|
28
|
-
catch (err) {
|
|
29
|
-
console.error(`✖ Validation failed: ${err.message}`);
|
|
30
|
-
exit(1);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
File without changes
|