@amritk/mjst 0.7.6 → 0.7.7

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.
@@ -1,180 +0,0 @@
1
- import type { SourceFormat } from '@amritk/adapters/source-format'
2
-
3
- import type { CliConfig } from './cli-config'
4
-
5
- const SOURCE_FORMATS: readonly SourceFormat[] = ['json', 'typebox', 'zod', 'valibot', 'effect']
6
-
7
- // Mutable shape used while building, returned as Partial<CliConfig>.
8
- type MutableConfig = {
9
- schema?: string
10
- schemaDir?: string
11
- outDir?: string
12
- outFile?: string
13
- input?: SourceFormat
14
- export?: string
15
- typesOnly?: boolean
16
- build?: boolean
17
- logWarnings?: boolean
18
- strict?: boolean
19
- readonly?: boolean
20
- helpers?: 'package' | 'embedded'
21
- typeSuffix?: string
22
- banner?: boolean | string
23
- }
24
-
25
- // Boolean flags toggle on by presence and accept `--flag=false` to opt out.
26
- const BOOLEAN_KEYS = new Set<keyof MutableConfig>(['typesOnly', 'build', 'logWarnings', 'strict', 'readonly'])
27
- // Value flags consume the following argument (or `--flag=value`).
28
- const VALUE_KEYS = new Set<keyof MutableConfig>([
29
- 'schema',
30
- 'schemaDir',
31
- 'outDir',
32
- 'outFile',
33
- 'input',
34
- 'export',
35
- 'helpers',
36
- 'typeSuffix',
37
- ])
38
-
39
- /** Normalizes a CLI flag name to its camelCase config key so both `--out-dir` and `--outDir` map to `outDir`. */
40
- const toCamelCase = (key: string): string => key.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase())
41
-
42
- const parseHelpersValue = (value: string): 'package' | 'embedded' | undefined => {
43
- if (value === 'package' || value === 'embedded') return value
44
- return undefined
45
- }
46
-
47
- const parseInputValue = (value: string): SourceFormat | undefined =>
48
- (SOURCE_FORMATS as readonly string[]).includes(value) ? (value as SourceFormat) : undefined
49
-
50
- /** Assigns a value-flag onto the config. Returns false for unknown keys. */
51
- const assignValue = (config: MutableConfig, key: string, value: string): boolean => {
52
- switch (key) {
53
- case 'schema':
54
- config.schema = value
55
- return true
56
- case 'schemaDir':
57
- config.schemaDir = value
58
- return true
59
- case 'outDir':
60
- config.outDir = value
61
- return true
62
- case 'outFile':
63
- config.outFile = value
64
- return true
65
- case 'export':
66
- config.export = value
67
- return true
68
- case 'typeSuffix':
69
- config.typeSuffix = value
70
- return true
71
- case 'input': {
72
- const parsed = parseInputValue(value)
73
- if (!parsed) {
74
- throw new Error(`Invalid --input value "${value}". Expected one of: ${SOURCE_FORMATS.join(', ')}.`)
75
- }
76
- config.input = parsed
77
- return true
78
- }
79
- case 'helpers': {
80
- const parsed = parseHelpersValue(value)
81
- if (!parsed) {
82
- throw new Error(`Invalid --helpers value "${value}". Expected one of: package, embedded.`)
83
- }
84
- config.helpers = parsed
85
- return true
86
- }
87
- default:
88
- return false
89
- }
90
- }
91
-
92
- /** Assigns a boolean flag onto the config. Returns false for unknown keys. */
93
- const assignBoolean = (config: MutableConfig, key: string, value: boolean): boolean => {
94
- switch (key) {
95
- case 'typesOnly':
96
- config.typesOnly = value
97
- return true
98
- case 'build':
99
- config.build = value
100
- return true
101
- case 'logWarnings':
102
- config.logWarnings = value
103
- return true
104
- case 'strict':
105
- config.strict = value
106
- return true
107
- case 'readonly':
108
- config.readonly = value
109
- return true
110
- default:
111
- return false
112
- }
113
- }
114
-
115
- /**
116
- * Parses command-line arguments into a partial CLI config.
117
- * Every flag accepts both kebab-case and camelCase (e.g. `--out-dir` and `--outDir`)
118
- * and supports either `--flag value` or `--flag=value` syntax. Boolean flags toggle
119
- * on by presence and accept `--flag=false` to opt out. Only flags that were explicitly
120
- * provided are returned so they can be layered on top of config file values.
121
- */
122
- export const parseCliArgs = (args: readonly string[]): Partial<CliConfig> => {
123
- const config: MutableConfig = {}
124
-
125
- for (let i = 0; i < args.length; i++) {
126
- const arg = args[i]
127
-
128
- if (!arg || !arg.startsWith('--')) {
129
- continue
130
- }
131
-
132
- // Handle --flag=value syntax
133
- const equalsIndex = arg.indexOf('=')
134
- if (equalsIndex !== -1) {
135
- const key = toCamelCase(arg.slice(2, equalsIndex))
136
- const value = arg.slice(equalsIndex + 1)
137
- if (key === 'banner') {
138
- // --banner=false → false, --banner=true → true, --banner=<text> → custom string
139
- config.banner = value === 'false' ? false : value === 'true' ? true : value
140
- } else if (BOOLEAN_KEYS.has(key as keyof MutableConfig)) {
141
- assignBoolean(config, key, value !== 'false')
142
- } else {
143
- assignValue(config, key, value)
144
- }
145
- continue
146
- }
147
-
148
- const key = toCamelCase(arg.slice(2))
149
-
150
- // --banner: presence alone enables the default message; an immediately
151
- // following non-flag argument is treated as a custom message string.
152
- if (key === 'banner') {
153
- const nextArg = args[i + 1]
154
- if (nextArg && !nextArg.startsWith('--')) {
155
- config.banner = nextArg
156
- i++
157
- } else {
158
- config.banner = true
159
- }
160
- continue
161
- }
162
-
163
- // Boolean flag: presence alone enables it, no value needed
164
- if (BOOLEAN_KEYS.has(key as keyof MutableConfig)) {
165
- assignBoolean(config, key, true)
166
- continue
167
- }
168
-
169
- // Value flag: consume the next argument when it is not another flag
170
- if (VALUE_KEYS.has(key as keyof MutableConfig)) {
171
- const value = args[i + 1]
172
- if (value && !value.startsWith('--')) {
173
- assignValue(config, key, value)
174
- i++
175
- }
176
- }
177
- }
178
-
179
- return config
180
- }
@@ -1,18 +0,0 @@
1
- import { readFile } from 'node:fs/promises'
2
- import { describe, expect, it } from 'vitest'
3
-
4
- import { readVersion } from './read-version'
5
-
6
- describe('read-version', () => {
7
- it('returns the version declared in the package.json', async () => {
8
- const pkg = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf-8')) as {
9
- version: string
10
- }
11
-
12
- expect(await readVersion()).toBe(pkg.version)
13
- })
14
-
15
- it('returns a semver-shaped string', async () => {
16
- expect(await readVersion()).toMatch(/^\d+\.\d+\.\d+/)
17
- })
18
- })
@@ -1,14 +0,0 @@
1
- import { readFile } from 'node:fs/promises'
2
-
3
- /**
4
- * Reads the CLI's own version from its `package.json`.
5
- *
6
- * Resolved relative to this module so it works whether the code runs from
7
- * `src/` (dev) or the bundled `dist/cli.js` (published) — `package.json`
8
- * sits one directory above both.
9
- */
10
- export const readVersion = async (): Promise<string> => {
11
- const pkgUrl = new URL('../package.json', import.meta.url)
12
- const pkg = JSON.parse(await readFile(pkgUrl, 'utf-8')) as { version?: string }
13
- return pkg.version ?? '0.0.0'
14
- }