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,33 @@
|
|
|
1
|
+
// prettier.config.js
|
|
2
|
+
/** @type {import("prettier").Config} */
|
|
3
|
+
module.exports = {
|
|
4
|
+
// ────────────────────────────────────────────────────────────────
|
|
5
|
+
// Formatting Options
|
|
6
|
+
// ─
|
|
7
|
+
useTabs: false,
|
|
8
|
+
|
|
9
|
+
// Maximum line length before Prettier wraps.
|
|
10
|
+
// "printWidth": 120,
|
|
11
|
+
printWidth: 80,
|
|
12
|
+
|
|
13
|
+
// Number of spaces per indentation level.
|
|
14
|
+
tabWidth: 4,
|
|
15
|
+
singleQuote: true,
|
|
16
|
+
trailingComma: "all",
|
|
17
|
+
jsxBracketSameLine: true,
|
|
18
|
+
semi: false,
|
|
19
|
+
// Whether to add a space between brackets in object literals
|
|
20
|
+
bracketSpacing: true,
|
|
21
|
+
// Since prettier 3.0, manually specifying plugins is required
|
|
22
|
+
plugins: ['@ianvs/prettier-plugin-sort-imports'],
|
|
23
|
+
// This plugin's options
|
|
24
|
+
importOrder: [
|
|
25
|
+
'^react$', // Put React first
|
|
26
|
+
'<THIRD_PARTY_MODULES>', // Then other external dependencies
|
|
27
|
+
'^[@/](.*)$', // Then “@/…” or “@alias/…” imports
|
|
28
|
+
'^[./]', // Then relative imports
|
|
29
|
+
],
|
|
30
|
+
importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'],
|
|
31
|
+
importOrderTypeScriptVersion: '5.0.0',
|
|
32
|
+
importOrderCaseSensitive: false,
|
|
33
|
+
}
|
package/sample.yini
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
@YINI
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Example of a YINI document.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
^ Service // Defines a section named Server.
|
|
8
|
+
Enabled = true
|
|
9
|
+
|
|
10
|
+
^^ Cache
|
|
11
|
+
Type = "redis" // Defines Cache, a sub-section of Server.
|
|
12
|
+
TTL = 3600
|
|
13
|
+
|
|
14
|
+
^^^ Options // Defines Options, a sub-section of Cache.
|
|
15
|
+
Host = "127.0.0.1"
|
|
16
|
+
Port = 6379
|
|
17
|
+
|
|
18
|
+
^ Env // Defines a section named Env.
|
|
19
|
+
code = "dev"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
nested.yini
|
|
3
|
+
Demonstrates nested sections in YINI format.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
@yini
|
|
7
|
+
|
|
8
|
+
^ App // Top-level section: App
|
|
9
|
+
name = 'Nested Demo App'
|
|
10
|
+
version = "1.2.3"
|
|
11
|
+
|
|
12
|
+
^^ Theme // Nested under App: App.Theme
|
|
13
|
+
primaryColor = #336699
|
|
14
|
+
darkMode = true
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
^^^ Overrides // Nested under Theme: App.Theme.Overrides
|
|
18
|
+
darkMode = false
|
|
19
|
+
fontSize = 14
|
|
20
|
+
|
|
21
|
+
^ Database // Another top-level section: Database
|
|
22
|
+
host = "db.local"
|
|
23
|
+
port = 5432
|
|
24
|
+
|
|
25
|
+
^^ Credentials // Nested under Database: Database.Credentials username = "admin"
|
|
26
|
+
password = "secret"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// import pkg from '../../package.json'
|
|
2
|
+
import { createRequire } from 'module'
|
|
3
|
+
|
|
4
|
+
const require = createRequire(import.meta.url)
|
|
5
|
+
const pkg = require('../../package.json')
|
|
6
|
+
|
|
7
|
+
// import pkg from '../../package.json' assert { type: 'json' }
|
|
8
|
+
|
|
9
|
+
export const printInfo = () => {
|
|
10
|
+
console.log(`** YINI CLI **`)
|
|
11
|
+
console.log(`yini-cli: ${pkg.version}`)
|
|
12
|
+
console.log(
|
|
13
|
+
`yini-parser: ${pkg.dependencies['yini-parser'].replace('^', '')}`,
|
|
14
|
+
)
|
|
15
|
+
console.log(`Author: ${pkg.author}`)
|
|
16
|
+
console.log(`License: ${pkg.license}`)
|
|
17
|
+
console.log(`Homepage: ${pkg.homepage}`)
|
|
18
|
+
console.log('Repo: https://github.com/YINI-lang/yini-cli')
|
|
19
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import util from 'util'
|
|
4
|
+
import YINI from 'yini-parser'
|
|
5
|
+
import { ICLIParseOptions, TBailSensitivity } from '../types.js'
|
|
6
|
+
import { printObject, toPrettyJSON } from '../utils/print.js'
|
|
7
|
+
|
|
8
|
+
type TOutputStype = 'JS-style' | 'Pretty-JSON' | 'Console.log' | 'JSON-compact'
|
|
9
|
+
|
|
10
|
+
export const parseFile = (file: string, options: ICLIParseOptions) => {
|
|
11
|
+
const outputFile = options.output || ''
|
|
12
|
+
const isStrictMode = !!options.strict
|
|
13
|
+
let outputStyle: TOutputStype = 'JS-style'
|
|
14
|
+
|
|
15
|
+
console.log('file = ' + file)
|
|
16
|
+
console.log('output = ' + options.output)
|
|
17
|
+
console.log('options:')
|
|
18
|
+
printObject(options)
|
|
19
|
+
|
|
20
|
+
if (options.pretty) {
|
|
21
|
+
outputStyle = 'Pretty-JSON'
|
|
22
|
+
} else if (options.log) {
|
|
23
|
+
outputStyle = 'Console.log'
|
|
24
|
+
} else if (options.json) {
|
|
25
|
+
outputStyle = 'JSON-compact'
|
|
26
|
+
} else {
|
|
27
|
+
outputStyle = 'JS-style'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
doParseFile(file, outputStyle, isStrictMode, outputFile)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const doParseFile = (
|
|
34
|
+
file: string,
|
|
35
|
+
outputStyle: TOutputStype,
|
|
36
|
+
isStrictMode = false,
|
|
37
|
+
outputFile = '',
|
|
38
|
+
) => {
|
|
39
|
+
// let strictMode = !!options.strict
|
|
40
|
+
let bailSensitivity: TBailSensitivity = 'auto'
|
|
41
|
+
let includeMetaData = false
|
|
42
|
+
|
|
43
|
+
console.log('File = ' + file)
|
|
44
|
+
console.log('outputStyle = ' + outputStyle)
|
|
45
|
+
|
|
46
|
+
// try {
|
|
47
|
+
// const raw = fs.readFileSync(file, 'utf-8')
|
|
48
|
+
// const parsed = YINI.parseFile(
|
|
49
|
+
//const parsed = YINI.parseFile(file)
|
|
50
|
+
const parsed = YINI.parseFile(
|
|
51
|
+
file,
|
|
52
|
+
isStrictMode,
|
|
53
|
+
bailSensitivity,
|
|
54
|
+
includeMetaData,
|
|
55
|
+
)
|
|
56
|
+
// const parsed = YINI.parse(raw)
|
|
57
|
+
|
|
58
|
+
// const output = options.pretty
|
|
59
|
+
// ? // ? JSON.stringify(parsed, null, 2)
|
|
60
|
+
// toPrettyJSON(parsed)
|
|
61
|
+
// : JSON.stringify(parsed)
|
|
62
|
+
let output = ''
|
|
63
|
+
switch (outputStyle) {
|
|
64
|
+
case 'Pretty-JSON':
|
|
65
|
+
output = toPrettyJSON(parsed)
|
|
66
|
+
break
|
|
67
|
+
case 'Console.log':
|
|
68
|
+
output = '<todo>'
|
|
69
|
+
break
|
|
70
|
+
case 'JSON-compact':
|
|
71
|
+
output = JSON.stringify(parsed)
|
|
72
|
+
break
|
|
73
|
+
default:
|
|
74
|
+
output = util.inspect(parsed, { depth: null, colors: false })
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (outputFile) {
|
|
78
|
+
// Write JSON output to file instead of stdout.
|
|
79
|
+
fs.writeFileSync(path.resolve(outputFile), output, 'utf-8')
|
|
80
|
+
console.log(`Output written to ${outputFile}`)
|
|
81
|
+
} else {
|
|
82
|
+
console.log(output)
|
|
83
|
+
}
|
|
84
|
+
// } catch (err: any) {
|
|
85
|
+
// console.error(`Error: ${err.message}`)
|
|
86
|
+
// process.exit(1)
|
|
87
|
+
// }
|
|
88
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
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
|
+
|
|
6
|
+
interface IValidateOptions {
|
|
7
|
+
strict?: boolean
|
|
8
|
+
details?: boolean
|
|
9
|
+
silent?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const validateFile = (file: string, options: IValidateOptions = {}) => {
|
|
13
|
+
try {
|
|
14
|
+
const content = fs.readFileSync(file, 'utf-8')
|
|
15
|
+
const isMeta = true
|
|
16
|
+
const parsed = YINI.parse(
|
|
17
|
+
content,
|
|
18
|
+
options.strict ?? false,
|
|
19
|
+
'auto',
|
|
20
|
+
isMeta,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if (!options.silent) {
|
|
24
|
+
console.log(
|
|
25
|
+
`✔ File is valid${options.strict ? ' (strict mode)' : ''}.`,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if (options.details) {
|
|
29
|
+
//@todo format parsed.meta to details as
|
|
30
|
+
/*
|
|
31
|
+
* Details:
|
|
32
|
+
* - YINI version: 1.0.0-beta.6
|
|
33
|
+
* - Mode: strict
|
|
34
|
+
* - Keys: 42
|
|
35
|
+
* - Sections: 6
|
|
36
|
+
* - Nesting depth: 3
|
|
37
|
+
* - Has @yini: true
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
printObject(parsed.meta)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exit(0)
|
|
44
|
+
} catch (err: any) {
|
|
45
|
+
console.error(`✖ Validation failed: ${err.message}`)
|
|
46
|
+
exit(1)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NODE_ENV - Defacto Node.js modes (environments)
|
|
3
|
+
*
|
|
4
|
+
* Used in many JS frameworks and tools, for special purposes.
|
|
5
|
+
* Some even only know 'production' and treat everything else as 'development'.
|
|
6
|
+
* Also Jest sets NODE_ENV automatically to 'test'.
|
|
7
|
+
*/
|
|
8
|
+
type TNodeEnv = 'development' | 'production' | 'test'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* APP_ENV - More custom envs (more finer-grained control) for this project.
|
|
12
|
+
* @note Since this is a library (as opposed to a Web/App), we don't use "staging".
|
|
13
|
+
*/
|
|
14
|
+
type TAppEnv = 'local' | 'ci' | 'production' // Note: 'staging' is omitted by purpose.
|
|
15
|
+
|
|
16
|
+
const localNodeEnv = (process.env.NODE_ENV || 'production') as TNodeEnv
|
|
17
|
+
const localAppEnv = (process.env.APP_ENV || 'production') as TAppEnv
|
|
18
|
+
|
|
19
|
+
// export const initEnvs = () => {
|
|
20
|
+
// const localNodeEnv = (process.env.NODE_ENV || 'production') as TNodeEnv
|
|
21
|
+
// const localAppEnv = (process.env?.APP_ENV || 'production') as TAppEnv
|
|
22
|
+
|
|
23
|
+
// return { localNodeEnv, localAppEnv }
|
|
24
|
+
// }
|
|
25
|
+
|
|
26
|
+
// const { localNodeEnv, localAppEnv } = initEnvs()
|
|
27
|
+
|
|
28
|
+
/** Are we running in the environment "development"? Will be based on the (global) environment variable process.env.NODE_ENV. */
|
|
29
|
+
export const isDevEnv = (): boolean => localNodeEnv === 'development'
|
|
30
|
+
|
|
31
|
+
/** Are we running in the environment "production"? Will be based on the (global) environment variable process.env.NODE_ENV. */
|
|
32
|
+
export const isProdEnv = (): boolean => localNodeEnv === 'production'
|
|
33
|
+
|
|
34
|
+
/** Are we running in the environment "test"? Will be based on the (global) variable process.env.NODE_ENV. */
|
|
35
|
+
export const isTestEnv = (): boolean => localNodeEnv === 'test'
|
|
36
|
+
|
|
37
|
+
/** Will be based on the local argument when this process was launched.
|
|
38
|
+
* @returns True if the DEV flag is set.
|
|
39
|
+
* @example npm run start -- isDev=1
|
|
40
|
+
* @example node dist/index.js isDev=1
|
|
41
|
+
*/
|
|
42
|
+
export const isDev = (): boolean => {
|
|
43
|
+
const len = process.argv.length
|
|
44
|
+
|
|
45
|
+
// NOTE: We will start with index 2, since the first element will be
|
|
46
|
+
// execPath. The second element will be the path to the
|
|
47
|
+
// JavaScript file being executed.
|
|
48
|
+
for (let i = 2; i < len; i++) {
|
|
49
|
+
const val: string = process.argv[i] || ''
|
|
50
|
+
if (
|
|
51
|
+
val.toLowerCase() === 'isdev=1' ||
|
|
52
|
+
val.toLowerCase() === 'isdev=true'
|
|
53
|
+
) {
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Will be based on the local argument when this process was launched.
|
|
62
|
+
* @returns True if the DEBUG flag is set.
|
|
63
|
+
* @example npm run start -- isDebug=1
|
|
64
|
+
* @example node dist/index.js isDebug=1
|
|
65
|
+
*/
|
|
66
|
+
export const isDebug = (): boolean => {
|
|
67
|
+
const len = process.argv.length
|
|
68
|
+
|
|
69
|
+
// NOTE: We will start with index 2, since the first element will be
|
|
70
|
+
// execPath. The second element will be the path to the
|
|
71
|
+
// JavaScript file being executed.
|
|
72
|
+
for (let i = 2; i < len; i++) {
|
|
73
|
+
const val: string = process.argv[i] || ''
|
|
74
|
+
if (
|
|
75
|
+
val.toLowerCase() === 'isdebug=1' ||
|
|
76
|
+
val.toLowerCase() === 'isdebug=true'
|
|
77
|
+
) {
|
|
78
|
+
return true
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export { localNodeEnv, localAppEnv }
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const descriptions = {
|
|
2
|
+
yini: 'CLI for parsing and validating YINI config files',
|
|
3
|
+
'For-command-info': 'Show extended information (details, links, etc.)',
|
|
4
|
+
'For-command-parse': 'Parse a YINI file and print the result',
|
|
5
|
+
'For-command-validate': 'Checks if the file can be parsed as valid YINI',
|
|
6
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// (!) NOTE: Leave above shebang as first line!
|
|
3
|
+
|
|
4
|
+
// import pkg from '../package.json'
|
|
5
|
+
import { createRequire } from 'module'
|
|
6
|
+
import { Command } from 'commander'
|
|
7
|
+
import { printInfo } from './commands/info.js'
|
|
8
|
+
import { parseFile } from './commands/parse.js'
|
|
9
|
+
import { validateFile } from './commands/validate.js'
|
|
10
|
+
import { isDebug } from './config/env.js'
|
|
11
|
+
import { descriptions as descripts } from './descriptions.js'
|
|
12
|
+
import { debugPrint, toPrettyJSON } from './utils/print.js'
|
|
13
|
+
|
|
14
|
+
const require = createRequire(import.meta.url)
|
|
15
|
+
const pkg = require('../package.json')
|
|
16
|
+
|
|
17
|
+
const program = new Command()
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
|
|
21
|
+
Idea/suggestion
|
|
22
|
+
yini [parse] [--strict] [--pretty] [--output]
|
|
23
|
+
|
|
24
|
+
Current suggestion:
|
|
25
|
+
* yini parse config.yini
|
|
26
|
+
JS-style object using printObject()
|
|
27
|
+
to stdout
|
|
28
|
+
* yini parse config.yini --pretty
|
|
29
|
+
Pretty JSON using JSON.stringify(obj, null, 4)
|
|
30
|
+
to stdout
|
|
31
|
+
* yini parse config.yini --output out.txt
|
|
32
|
+
JS-style object
|
|
33
|
+
to out.txt
|
|
34
|
+
* yini parse config.yini --pretty --output out.json
|
|
35
|
+
Pretty JSON
|
|
36
|
+
to out.json
|
|
37
|
+
|
|
38
|
+
New suggestion:
|
|
39
|
+
Current suggestion:
|
|
40
|
+
* yini parse config.yini
|
|
41
|
+
JS-style object using printObject(obj) (using using util.inspect)
|
|
42
|
+
to stdout
|
|
43
|
+
* yini parse config.yini --pretty
|
|
44
|
+
Pretty JSON using JSON.stringify(obj, null, 4) (formatted, readable)
|
|
45
|
+
to stdout
|
|
46
|
+
* yini parse config.yini --log
|
|
47
|
+
Intended for quick output using console.log (nested object may get compacted/abbreviate)
|
|
48
|
+
to stdout
|
|
49
|
+
* yini parse config.yini --json
|
|
50
|
+
Stringigies JSON using using JSON.stringify(obj) (compact, machine-parseable)
|
|
51
|
+
to stdout
|
|
52
|
+
* yini parse config.yini --output out.txt
|
|
53
|
+
JS-style object
|
|
54
|
+
to out.txt
|
|
55
|
+
* yini parse config.yini --pretty --output out.json
|
|
56
|
+
Pretty JSON
|
|
57
|
+
to out.json
|
|
58
|
+
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
// Display help for command
|
|
62
|
+
program.name('yini').description(descripts.yini).version(pkg.version)
|
|
63
|
+
|
|
64
|
+
program.addHelpText(
|
|
65
|
+
'before',
|
|
66
|
+
`YINI CLI (Yet another INI)
|
|
67
|
+
|
|
68
|
+
For parsing and validating YINI configuration files.
|
|
69
|
+
A human-friendly config format - like INI, but with type-safe values,
|
|
70
|
+
nested sections, comments, minimal syntax noise, and optional strict mode.
|
|
71
|
+
|
|
72
|
+
Crafted for clarity, consistency, and the simple joy of it. :)`,
|
|
73
|
+
)
|
|
74
|
+
program.addHelpText(
|
|
75
|
+
'after',
|
|
76
|
+
`
|
|
77
|
+
Examples:
|
|
78
|
+
$ yini parse config.yini
|
|
79
|
+
$ yini validate config.yini --strict
|
|
80
|
+
$ yini parse config.yini --pretty --output out.json
|
|
81
|
+
|
|
82
|
+
More info: https://github.com/YINI-lang/yini-parser
|
|
83
|
+
`,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
//program.command('help [command]').description('Display help for command')
|
|
87
|
+
|
|
88
|
+
// Command info
|
|
89
|
+
program
|
|
90
|
+
.command('info')
|
|
91
|
+
// .command('')
|
|
92
|
+
.description(descripts['For-command-info'])
|
|
93
|
+
// .option('info')
|
|
94
|
+
.action((options) => {
|
|
95
|
+
debugPrint('Run command "info"')
|
|
96
|
+
if (isDebug()) {
|
|
97
|
+
console.log('options:')
|
|
98
|
+
console.log(toPrettyJSON(options))
|
|
99
|
+
}
|
|
100
|
+
printInfo()
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
*
|
|
105
|
+
* Maybe later, to as default command: parse <parse>
|
|
106
|
+
*/
|
|
107
|
+
// program
|
|
108
|
+
// .argument('<file>', 'File to parse')
|
|
109
|
+
// .option('--strict', 'Parse YINI in strict-mode')
|
|
110
|
+
// .option('--pretty', 'Pretty-print output as JSON')
|
|
111
|
+
// // .option('--log', 'Use console.log output format (compact, quick view)')
|
|
112
|
+
// .option('--json', 'Compact JSON output using JSON.stringify')
|
|
113
|
+
// .option('--output <file>', 'Write output to a specified file')
|
|
114
|
+
// .action((file, options) => {
|
|
115
|
+
// if (file) {
|
|
116
|
+
// parseFile(file, options)
|
|
117
|
+
// } else {
|
|
118
|
+
// program.help()
|
|
119
|
+
// }
|
|
120
|
+
// })
|
|
121
|
+
|
|
122
|
+
// Explicit "parse" command
|
|
123
|
+
program
|
|
124
|
+
.command('parse <file>')
|
|
125
|
+
.description(descripts['For-command-parse'])
|
|
126
|
+
.option('--strict', 'Parse YINI in strict-mode')
|
|
127
|
+
.option('--pretty', 'Pretty-print output as JSON')
|
|
128
|
+
// .option('--log', 'Use console.log output format (compact, quick view)')
|
|
129
|
+
.option('--json', 'Compact JSON output using JSON.stringify')
|
|
130
|
+
.option('--output <file>', 'Write output to a specified file')
|
|
131
|
+
.action((file, options) => {
|
|
132
|
+
debugPrint('Run command "parse"')
|
|
133
|
+
debugPrint(`<file> = ${file}`)
|
|
134
|
+
if (isDebug()) {
|
|
135
|
+
console.log('options:')
|
|
136
|
+
console.log(toPrettyJSON(options))
|
|
137
|
+
}
|
|
138
|
+
if (file) {
|
|
139
|
+
parseFile(file, options)
|
|
140
|
+
} else {
|
|
141
|
+
program.help()
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* To handle command validate, e.g.:
|
|
147
|
+
* yini validate config.yini
|
|
148
|
+
* yini validate config.yini --strict
|
|
149
|
+
* yini validate config.yini --details
|
|
150
|
+
* yini validate config.yini --silent
|
|
151
|
+
*
|
|
152
|
+
* If details:
|
|
153
|
+
* Details:
|
|
154
|
+
* - YINI version: 1.0.0-beta.6
|
|
155
|
+
* - Mode: strict
|
|
156
|
+
* - Keys: 42
|
|
157
|
+
* - Sections: 6
|
|
158
|
+
* - Nesting depth: 3
|
|
159
|
+
* - Has @yini: true
|
|
160
|
+
*/
|
|
161
|
+
program
|
|
162
|
+
.command('validate <file>')
|
|
163
|
+
.description(descripts['For-command-validate'])
|
|
164
|
+
.option('--strict', 'Enable parsing in strict-mode')
|
|
165
|
+
.option(
|
|
166
|
+
'--details',
|
|
167
|
+
'Print detailed meta-data info (e.g., key count, nesting, etc.)',
|
|
168
|
+
)
|
|
169
|
+
.option('--silent', 'Suppress output')
|
|
170
|
+
.action((file, options) => {
|
|
171
|
+
//@todo add debugPrint
|
|
172
|
+
if (file) {
|
|
173
|
+
validateFile(file, options)
|
|
174
|
+
} else {
|
|
175
|
+
program.help()
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
// NOTE: Converting YINI files to other formats than json and js.
|
|
180
|
+
// Other format should go into a new CLI-command called 'yini-convert'.
|
|
181
|
+
|
|
182
|
+
program.parseAsync()
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file contains general system helper functions (utils).
|
|
3
|
+
* @note More specific YINI helper functions should go into yiniHelpers.ts-file.
|
|
4
|
+
*/
|
|
5
|
+
import util from 'util'
|
|
6
|
+
import { isDebug, isDev, isProdEnv, isTestEnv } from '../config/env.js'
|
|
7
|
+
|
|
8
|
+
// import { isDebug, isDev, isProdEnv, isTestEnv } from '../config/env'
|
|
9
|
+
|
|
10
|
+
export const debugPrint = (str: any = '') => {
|
|
11
|
+
isDebug() && console.debug('DEBUG: ' + str)
|
|
12
|
+
console.debug('DEBUG: ' + str)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const devPrint = (str: any = '') => {
|
|
16
|
+
isDev() && !isTestEnv() && console.log('DEV: ' + str)
|
|
17
|
+
console.log('DEV: ' + str)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const toJSON = (obj: any): string => {
|
|
21
|
+
const str = JSON.stringify(obj)
|
|
22
|
+
return str
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const toPrettyJSON = (obj: any): string => {
|
|
26
|
+
const str = JSON.stringify(obj, null, 4)
|
|
27
|
+
return str
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Pretty-prints a JavaScript object as formatted JSON to the console.
|
|
31
|
+
* Strict JSON, all keys are enclosed in ", etc.
|
|
32
|
+
*/
|
|
33
|
+
export const printJSON = (obj: any) => {
|
|
34
|
+
if (isProdEnv() || (isTestEnv() && !isDebug())) return
|
|
35
|
+
|
|
36
|
+
const str = toPrettyJSON(obj)
|
|
37
|
+
console.log(str)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Print a full JavaScript object in a human-readable way (not as JSON).
|
|
42
|
+
* Not strict JSON, and shows functions, symbols, getters/setters, and class names.
|
|
43
|
+
* @param isColors If true, the output is styled with ANSI color codes.
|
|
44
|
+
*/
|
|
45
|
+
export const printObject = (obj: any, isColors = true) => {
|
|
46
|
+
if (isProdEnv() || (isTestEnv() && !isDebug())) return
|
|
47
|
+
|
|
48
|
+
console.log(util.inspect(obj, { depth: null, colors: isColors }))
|
|
49
|
+
}
|