|
1 | 1 | /* eslint-disable no-console */ |
2 | | -import {parseArgs} from 'node:util' |
3 | 2 |
|
4 | 3 | // eslint-disable-next-line import-x/no-extraneous-dependencies -- bundled, not a runtime dep |
5 | 4 | import {isInteractive} from '@sanity/cli-core' |
6 | | -// eslint-disable-next-line import-x/no-extraneous-dependencies -- bundled, not a runtime dep |
7 | | -import {getRunningPackageManager} from '@sanity/cli-core/package-manager' |
8 | 5 |
|
9 | | -import {type FlagDef, initFlagDefs} from '../../@sanity/cli/src/actions/init/flags.js' |
10 | | -import {initAction} from '../../@sanity/cli/src/actions/init/initAction.js' |
11 | | -import {InitError} from '../../@sanity/cli/src/actions/init/initError.js' |
12 | 6 | import { |
13 | 7 | flagsToInitOptions, |
14 | 8 | type InitCommandFlags, |
15 | 9 | } from '../../@sanity/cli/src/actions/init/flagsToInitOptions.js' |
| 10 | +import {initAction} from '../../@sanity/cli/src/actions/init/initAction.js' |
| 11 | +import {InitError} from '../../@sanity/cli/src/actions/init/initError.js' |
16 | 12 | import {createNoopTelemetryStore} from './noopTelemetry.js' |
17 | | - |
18 | | -function getCreateCommand(options?: {withFlagSeparator?: boolean}): string { |
19 | | - const pm = getRunningPackageManager() ?? 'npm' |
20 | | - // npm requires `--` to forward flags to the create script, other PMs don't |
21 | | - const sep = options?.withFlagSeparator && (pm === 'npm' || !pm) ? ' --' : '' |
22 | | - if (pm === 'bun') return `bun create sanity@latest${sep}` |
23 | | - if (pm === 'pnpm') return `pnpm create sanity@latest${sep}` |
24 | | - if (pm === 'yarn') return `yarn create sanity@latest${sep}` |
25 | | - return `npm create sanity@latest${sep}` |
26 | | -} |
27 | | - |
28 | | -type ParseArgsOption = { |
29 | | - default?: boolean | string |
30 | | - multiple?: boolean |
31 | | - short?: string |
32 | | - type: 'boolean' | 'string' |
33 | | -} |
34 | | - |
35 | | -function buildParseArgsOptions() { |
36 | | - const options: Record<string, ParseArgsOption> = {} |
37 | | - const allowNoFlags = new Set<string>() |
38 | | - // Maps alias name → canonical flag name (e.g. 'project-id' → 'project') |
39 | | - const aliasMap = new Map<string, string>() |
40 | | - |
41 | | - for (const [name, def] of Object.entries<FlagDef>(initFlagDefs)) { |
42 | | - if (def.type !== 'boolean' && def.type !== 'string') { |
43 | | - throw new Error(`Unknown flag type "${def.type}" for flag "${name}"`) |
44 | | - } |
45 | | - |
46 | | - options[name] = {type: def.type} |
47 | | - if (def.short) options[name].short = def.short |
48 | | - if (def.default !== undefined) options[name].default = def.default |
49 | | - |
50 | | - if (def.type === 'boolean' && def.allowNo) { |
51 | | - allowNoFlags.add(name) |
52 | | - options[`no-${name}`] = {type: 'boolean'} |
53 | | - } |
54 | | - |
55 | | - // Register aliases as separate parseArgs options that map back to the canonical name |
56 | | - if (def.aliases) { |
57 | | - for (const alias of def.aliases) { |
58 | | - options[alias] = {type: def.type} |
59 | | - aliasMap.set(alias, name) |
60 | | - } |
61 | | - } |
62 | | - } |
63 | | - |
64 | | - // Built-in --help support |
65 | | - options.help = {short: 'h', type: 'boolean'} |
66 | | - |
67 | | - return {aliasMap, allowNoFlags, options} |
68 | | -} |
69 | | - |
70 | | -/** |
71 | | - * Merge --no-<flag> companions back into the base flag, resolve aliases |
72 | | - * to canonical names, and validate option constraints. |
73 | | - */ |
74 | | -function normalizeFlags( |
75 | | - values: Record<string, unknown>, |
76 | | - allowNoFlags: Set<string>, |
77 | | - aliasMap: Map<string, string>, |
78 | | -): Record<string, unknown> { |
79 | | - const merged = {...values} |
80 | | - |
81 | | - // Resolve aliases to canonical names |
82 | | - for (const [alias, canonical] of aliasMap) { |
83 | | - if (merged[alias] !== undefined) { |
84 | | - merged[canonical] = merged[alias] |
85 | | - delete merged[alias] |
86 | | - } |
87 | | - } |
88 | | - |
89 | | - // Merge --no-<flag> companions |
90 | | - for (const name of allowNoFlags) { |
91 | | - const noKey = `no-${name}` |
92 | | - if (merged[noKey] === true) { |
93 | | - merged[name] = false |
94 | | - } |
95 | | - delete merged[noKey] |
96 | | - } |
97 | | - |
98 | | - // Validate string flags with `options` constraints |
99 | | - for (const [name, def] of Object.entries<FlagDef>(initFlagDefs)) { |
100 | | - if (def.options && merged[name] !== undefined) { |
101 | | - const value = String(merged[name]) |
102 | | - if (!def.options.includes(value)) { |
103 | | - console.error( |
104 | | - `Invalid value "${value}" for --${name}. ` + `Allowed: ${def.options.join(', ')}`, |
105 | | - ) |
106 | | - process.exit(1) |
107 | | - } |
108 | | - } |
109 | | - } |
110 | | - |
111 | | - // Validate exclusive constraints |
112 | | - for (const [name, def] of Object.entries<FlagDef>(initFlagDefs)) { |
113 | | - if (!def.exclusive || merged[name] === undefined) continue |
114 | | - for (const other of def.exclusive) { |
115 | | - if (merged[other] !== undefined) { |
116 | | - console.error(`--${name} cannot be used with --${other}`) |
117 | | - process.exit(1) |
118 | | - } |
119 | | - } |
120 | | - } |
121 | | - |
122 | | - return merged |
123 | | -} |
| 13 | +import {getCreateCommand, parseInitArgs} from './parseArgs.js' |
124 | 14 |
|
125 | 15 | try { |
126 | | - const {aliasMap, allowNoFlags, options} = buildParseArgsOptions() |
127 | | - const {positionals, values} = parseArgs({ |
128 | | - allowPositionals: true, |
129 | | - args: process.argv.slice(2), |
130 | | - options, |
131 | | - strict: true, |
132 | | - }) |
133 | | - |
134 | | - if (values.help) { |
135 | | - const cmd = getCreateCommand({withFlagSeparator: true}) |
136 | | - console.log(`Usage: ${cmd} [options]`) |
137 | | - console.log('') |
138 | | - console.log('Initialize a new Sanity project') |
139 | | - console.log('') |
140 | | - console.log('Options:') |
141 | | - for (const [name, def] of Object.entries<FlagDef>(initFlagDefs)) { |
142 | | - if (def.hidden) continue |
143 | | - const flag = def.short ? `-${def.short}, --${name}` : ` --${name}` |
144 | | - const val = def.type === 'string' && def.helpValue ? ` ${def.helpValue}` : '' |
145 | | - console.log(` ${(flag + val).padEnd(36)} ${def.description || ''}`) |
146 | | - } |
147 | | - process.exit(0) |
148 | | - } |
149 | | - |
150 | | - const flags = normalizeFlags(values, allowNoFlags, aliasMap) |
151 | | - const args = {type: positionals[0]} |
| 16 | + const {args, flags} = parseInitArgs(process.argv.slice(2)) |
152 | 17 |
|
153 | 18 | let mcpMode: 'auto' | 'prompt' | 'skip' = 'prompt' |
154 | 19 | if (!flags.mcp || !isInteractive()) { |
|
168 | 33 |
|
169 | 34 | await initAction(initOptions, { |
170 | 35 | output: { |
171 | | - error: (msg: string): never => { |
172 | | - console.error(msg) |
| 36 | + error: (msg: Error | string): never => { |
| 37 | + console.error(msg instanceof Error ? msg.message : msg) |
173 | 38 | process.exit(1) |
174 | 39 | }, |
175 | 40 | log: console.log, |
|
0 commit comments