@remix-run/cli 0.3.3 → 0.4.0
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 +105 -1
- package/dist/index.js +1 -1
- package/dist/lib/app-root.d.ts +11 -0
- package/dist/lib/app-root.d.ts.map +1 -0
- package/dist/lib/app-root.js +38 -0
- package/dist/lib/bootstrap-project.js +2 -2
- package/dist/lib/cli-context.d.ts +6 -0
- package/dist/lib/cli-context.d.ts.map +1 -1
- package/dist/lib/cli-context.js +12 -1
- package/dist/lib/cli.d.ts +3 -2
- package/dist/lib/cli.d.ts.map +1 -1
- package/dist/lib/cli.js +42 -14
- package/dist/lib/commands/completion.js +4 -4
- package/dist/lib/commands/db.d.ts +4 -0
- package/dist/lib/commands/db.d.ts.map +1 -0
- package/dist/lib/commands/db.js +283 -0
- package/dist/lib/commands/doctor.d.ts +5 -0
- package/dist/lib/commands/doctor.d.ts.map +1 -1
- package/dist/lib/commands/doctor.js +26 -251
- package/dist/lib/commands/help.d.ts.map +1 -1
- package/dist/lib/commands/help.js +17 -9
- package/dist/lib/commands/new.js +6 -6
- package/dist/lib/commands/routes.js +6 -6
- package/dist/lib/commands/test.d.ts +20 -1
- package/dist/lib/commands/test.d.ts.map +1 -1
- package/dist/lib/commands/test.js +353 -10
- package/dist/lib/commands/version.js +3 -3
- package/dist/lib/completion.d.ts +1 -1
- package/dist/lib/completion.d.ts.map +1 -1
- package/dist/lib/completion.js +229 -6
- package/dist/lib/controller-files.d.ts +0 -8
- package/dist/lib/controller-files.d.ts.map +1 -1
- package/dist/lib/controller-files.js +0 -23
- package/dist/lib/controller-ownership.d.ts +4 -12
- package/dist/lib/controller-ownership.d.ts.map +1 -1
- package/dist/lib/controller-ownership.js +31 -127
- package/dist/lib/database-command.d.ts +20 -0
- package/dist/lib/database-command.d.ts.map +1 -0
- package/dist/lib/database-command.js +7 -0
- package/dist/lib/doctor/controller-findings.d.ts +2 -2
- package/dist/lib/doctor/controller-findings.d.ts.map +1 -1
- package/dist/lib/doctor/controller-findings.js +23 -39
- package/dist/lib/doctor/controllers.d.ts +1 -2
- package/dist/lib/doctor/controllers.d.ts.map +1 -1
- package/dist/lib/doctor/controllers.js +4 -7
- package/dist/lib/doctor/environment.js +2 -2
- package/dist/lib/doctor/fixes.d.ts.map +1 -1
- package/dist/lib/doctor/fixes.js +46 -7
- package/dist/lib/doctor/project.d.ts.map +1 -1
- package/dist/lib/doctor/project.js +3 -212
- package/dist/lib/doctor/run.d.ts +9 -0
- package/dist/lib/doctor/run.d.ts.map +1 -0
- package/dist/lib/doctor/run.js +237 -0
- package/dist/lib/doctor/types.d.ts +2 -1
- package/dist/lib/doctor/types.d.ts.map +1 -1
- package/dist/lib/doctor/types.js +4 -1
- package/dist/lib/errors.d.ts +30 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +55 -0
- package/dist/lib/help-text.js +1 -1
- package/dist/lib/parse-args.d.ts.map +1 -1
- package/dist/lib/parse-args.js +17 -4
- package/dist/lib/remix-config.d.ts +75 -0
- package/dist/lib/remix-config.d.ts.map +1 -0
- package/dist/lib/remix-config.js +411 -0
- package/dist/lib/reporter.js +1 -1
- package/dist/lib/route-map.js +13 -33
- package/package.json +12 -6
- package/schema/remix.json +242 -0
- package/src/lib/app-root.ts +46 -0
- package/src/lib/cli-context.ts +17 -0
- package/src/lib/cli.ts +41 -6
- package/src/lib/commands/db.ts +377 -0
- package/src/lib/commands/doctor.ts +27 -329
- package/src/lib/commands/help.ts +10 -1
- package/src/lib/commands/test.ts +453 -12
- package/src/lib/completion.ts +321 -6
- package/src/lib/controller-files.ts +0 -38
- package/src/lib/controller-ownership.ts +50 -203
- package/src/lib/database-command.ts +31 -0
- package/src/lib/doctor/controller-findings.ts +30 -47
- package/src/lib/doctor/controllers.ts +2 -6
- package/src/lib/doctor/fixes.ts +55 -6
- package/src/lib/doctor/project.ts +0 -236
- package/src/lib/doctor/run.ts +311 -0
- package/src/lib/doctor/types.ts +6 -5
- package/src/lib/errors.ts +65 -0
- package/src/lib/parse-args.ts +19 -3
- package/src/lib/remix-config.ts +639 -0
- package/src/lib/route-map.ts +11 -48
- package/template/.agents/skills/remix/SKILL.md +39 -28
- package/template/.agents/skills/remix/references/assets-and-browser-modules.md +53 -10
- package/template/.agents/skills/remix/references/auth-and-sessions.md +5 -5
- package/template/.agents/skills/remix/references/data-and-validation.md +15 -15
- package/template/.agents/skills/remix/references/hydration-frames-navigation.md +54 -8
- package/template/.agents/skills/remix/references/middleware-and-server.md +68 -6
- package/template/.agents/skills/remix/references/mixins-styling-events.md +2 -0
- package/template/.agents/skills/remix/references/routing-and-controllers.md +8 -8
- package/template/.agents/skills/remix/references/testing-patterns.md +19 -18
- package/template/AGENTS.md +6 -3
- package/template/README.md +6 -3
- package/template/app/actions/controller.tsx +1 -1
- package/template/app/{ui → actions}/document.tsx +7 -5
- package/template/app/{ui/scaffold-home-page.tsx → actions/home-page.tsx} +2 -3
- package/template/app/actions/public/entry.ts +48 -0
- package/template/app/{assets → actions/public}/prompt-button.tsx +29 -25
- package/template/app/assets.ts +19 -8
- package/template/app/middleware/render.tsx +29 -1
- package/template/hmr.ts +46 -0
- package/template/package.json +4 -3
- package/template/server.ts +8 -1
- package/template/tsconfig.json +1 -1
- package/dist/lib/doctor/controller-fix-plans.d.ts +0 -4
- package/dist/lib/doctor/controller-fix-plans.d.ts.map +0 -1
- package/dist/lib/doctor/controller-fix-plans.js +0 -119
- package/dist/lib/doctor/controller-placeholders.d.ts +0 -3
- package/dist/lib/doctor/controller-placeholders.d.ts.map +0 -1
- package/dist/lib/doctor/controller-placeholders.js +0 -52
- package/src/lib/doctor/controller-fix-plans.ts +0 -168
- package/src/lib/doctor/controller-placeholders.ts +0 -70
- package/template/app/assets/entry.ts +0 -8
package/src/lib/completion.ts
CHANGED
|
@@ -1,11 +1,54 @@
|
|
|
1
|
+
import { testCommandFlags } from './commands/test.ts'
|
|
2
|
+
|
|
1
3
|
export interface CompletionResult {
|
|
2
4
|
mode: 'files' | 'none' | 'values'
|
|
3
5
|
values?: string[]
|
|
4
6
|
}
|
|
5
7
|
|
|
6
8
|
const COMPLETION_SHELLS = ['bash', 'zsh'] as const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
+
const DB_COMMANDS = ['migrate', 'reset', 'seed', 'status', 'wipe'] as const
|
|
10
|
+
const HELP_COMMANDS = [
|
|
11
|
+
'completion',
|
|
12
|
+
'db',
|
|
13
|
+
'doctor',
|
|
14
|
+
'help',
|
|
15
|
+
'new',
|
|
16
|
+
'routes',
|
|
17
|
+
'test',
|
|
18
|
+
'version',
|
|
19
|
+
] as const
|
|
20
|
+
const ROOT_COMMANDS = [
|
|
21
|
+
'completion',
|
|
22
|
+
'db',
|
|
23
|
+
'doctor',
|
|
24
|
+
'help',
|
|
25
|
+
'new',
|
|
26
|
+
'routes',
|
|
27
|
+
'test',
|
|
28
|
+
'version',
|
|
29
|
+
] as const
|
|
30
|
+
|
|
31
|
+
// Derived from the `remix test` flag table so completion stays in sync with
|
|
32
|
+
// argument parsing and help text.
|
|
33
|
+
const TEST_BOOLEAN_FLAGS = testCommandFlags
|
|
34
|
+
.filter((flag) => flag.type === 'boolean')
|
|
35
|
+
.map((flag) => flag.name)
|
|
36
|
+
const TEST_VALUE_FLAGS = testCommandFlags
|
|
37
|
+
.filter((flag) => flag.type === 'string')
|
|
38
|
+
.map((flag) => flag.name)
|
|
39
|
+
const TEST_BOOLEAN_FLAG_SET = new Set(TEST_BOOLEAN_FLAGS)
|
|
40
|
+
const TEST_VALUE_FLAG_SET = new Set(TEST_VALUE_FLAGS)
|
|
41
|
+
const TEST_REPEATABLE_FLAGS = new Set(
|
|
42
|
+
testCommandFlags.filter((flag) => flag.multiple).map((flag) => flag.name),
|
|
43
|
+
)
|
|
44
|
+
const TEST_FILE_VALUE_FLAGS = new Set(
|
|
45
|
+
testCommandFlags.filter((flag) => flag.files).map((flag) => flag.name),
|
|
46
|
+
)
|
|
47
|
+
const TEST_FLAG_ALIASES = new Map(
|
|
48
|
+
testCommandFlags.flatMap((flag) =>
|
|
49
|
+
flag.alias === undefined ? [] : [[flag.alias, flag.name] as const],
|
|
50
|
+
),
|
|
51
|
+
)
|
|
9
52
|
|
|
10
53
|
export type CompletionShell = (typeof COMPLETION_SHELLS)[number]
|
|
11
54
|
|
|
@@ -18,6 +61,10 @@ export function getCompletionResult(words: string[], currentIndex: number): Comp
|
|
|
18
61
|
let currentWord = words[resolvedIndex] ?? ''
|
|
19
62
|
let tokens = getTokensBeforeCursor(words, resolvedIndex)
|
|
20
63
|
|
|
64
|
+
if (currentWord.startsWith('--config=')) {
|
|
65
|
+
return { mode: 'files' }
|
|
66
|
+
}
|
|
67
|
+
|
|
21
68
|
return completeTopLevel(tokens, currentWord)
|
|
22
69
|
}
|
|
23
70
|
|
|
@@ -47,7 +94,9 @@ if type complete &>/dev/null; then
|
|
|
47
94
|
local lines
|
|
48
95
|
|
|
49
96
|
if type _get_comp_words_by_ref &>/dev/null; then
|
|
50
|
-
|
|
97
|
+
# -n = keeps --config=<path> together as one word; bash's default
|
|
98
|
+
# COMP_WORDBREAKS would otherwise split it at the equals sign.
|
|
99
|
+
_get_comp_words_by_ref -n = -w words -i cword
|
|
51
100
|
else
|
|
52
101
|
cword="$COMP_CWORD"
|
|
53
102
|
words=("\${COMP_WORDS[@]}")
|
|
@@ -66,7 +115,15 @@ if type complete &>/dev/null; then
|
|
|
66
115
|
fi
|
|
67
116
|
|
|
68
117
|
if [[ "$mode" == "mode:files" ]]; then
|
|
69
|
-
|
|
118
|
+
if [[ "$current" == --config=* ]]; then
|
|
119
|
+
# Readline still breaks the insertion point at '=', so complete the
|
|
120
|
+
# path portion alone; prefixing --config= would duplicate the flag.
|
|
121
|
+
local config_value
|
|
122
|
+
config_value="\${current#--config=}"
|
|
123
|
+
COMPREPLY=($(compgen -f -- "$config_value"))
|
|
124
|
+
else
|
|
125
|
+
COMPREPLY=($(compgen -f -- "$current"))
|
|
126
|
+
fi
|
|
70
127
|
return 0
|
|
71
128
|
fi
|
|
72
129
|
|
|
@@ -92,6 +149,9 @@ elif type compdef &>/dev/null; then
|
|
|
92
149
|
fi
|
|
93
150
|
|
|
94
151
|
if [[ "$mode" == "mode:files" ]]; then
|
|
152
|
+
if [[ "\${words[CURRENT]}" == --config=* ]]; then
|
|
153
|
+
compset -P '--config='
|
|
154
|
+
fi
|
|
95
155
|
if autoload -U +X _files 2>/dev/null; then
|
|
96
156
|
_files
|
|
97
157
|
else
|
|
@@ -116,6 +176,19 @@ function completeTopLevel(tokens: string[], currentWord: string): CompletionResu
|
|
|
116
176
|
while (index < tokens.length) {
|
|
117
177
|
let token = tokens[index]
|
|
118
178
|
|
|
179
|
+
if (token === '--config') {
|
|
180
|
+
usedFlags.add('--config')
|
|
181
|
+
if (tokens[index + 1] === undefined) return { mode: 'files' }
|
|
182
|
+
index += 2
|
|
183
|
+
continue
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (token.startsWith('--config=')) {
|
|
187
|
+
usedFlags.add('--config')
|
|
188
|
+
index++
|
|
189
|
+
continue
|
|
190
|
+
}
|
|
191
|
+
|
|
119
192
|
if (token === '--no-color') {
|
|
120
193
|
usedFlags.add('--no-color')
|
|
121
194
|
index++
|
|
@@ -144,6 +217,10 @@ function completeCommand(
|
|
|
144
217
|
currentWord: string,
|
|
145
218
|
usedGlobalFlags: Set<string>,
|
|
146
219
|
): CompletionResult {
|
|
220
|
+
if (expectsGlobalConfigValue(tokens)) {
|
|
221
|
+
return { mode: 'files' }
|
|
222
|
+
}
|
|
223
|
+
|
|
147
224
|
if (command === 'help') {
|
|
148
225
|
return completeHelp(tokens, currentWord, usedGlobalFlags)
|
|
149
226
|
}
|
|
@@ -152,11 +229,16 @@ function completeCommand(
|
|
|
152
229
|
return completeNew(tokens, currentWord, usedGlobalFlags)
|
|
153
230
|
}
|
|
154
231
|
|
|
232
|
+
if (command === 'db') {
|
|
233
|
+
return completeDb(tokens, currentWord, usedGlobalFlags)
|
|
234
|
+
}
|
|
235
|
+
|
|
155
236
|
if (command === 'doctor') {
|
|
156
237
|
return completeSimpleFlags(tokens, currentWord, usedGlobalFlags, [
|
|
157
238
|
'--fix',
|
|
158
239
|
'--json',
|
|
159
240
|
'--strict',
|
|
241
|
+
'--no-strict',
|
|
160
242
|
])
|
|
161
243
|
}
|
|
162
244
|
|
|
@@ -169,7 +251,7 @@ function completeCommand(
|
|
|
169
251
|
}
|
|
170
252
|
|
|
171
253
|
if (command === 'test') {
|
|
172
|
-
return
|
|
254
|
+
return completeTest(tokens, currentWord, usedGlobalFlags)
|
|
173
255
|
}
|
|
174
256
|
|
|
175
257
|
if (command === 'completion') {
|
|
@@ -179,6 +261,108 @@ function completeCommand(
|
|
|
179
261
|
return completeValues([], currentWord)
|
|
180
262
|
}
|
|
181
263
|
|
|
264
|
+
function completeTest(
|
|
265
|
+
tokens: string[],
|
|
266
|
+
currentWord: string,
|
|
267
|
+
usedGlobalFlags: Set<string>,
|
|
268
|
+
): CompletionResult {
|
|
269
|
+
let filteredTokens = filterGlobalCommandTokens(tokens, usedGlobalFlags)
|
|
270
|
+
if (filteredTokens == null) {
|
|
271
|
+
return completeValues([], currentWord)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
let usedFlags = new Set<string>()
|
|
275
|
+
let expectedValueFor: string | undefined
|
|
276
|
+
let afterSeparator = false
|
|
277
|
+
|
|
278
|
+
for (let token of filteredTokens) {
|
|
279
|
+
if (expectedValueFor != null) {
|
|
280
|
+
expectedValueFor = undefined
|
|
281
|
+
continue
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Everything after a bare `--` is a positional test file glob
|
|
285
|
+
if (afterSeparator) {
|
|
286
|
+
continue
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (token === '--') {
|
|
290
|
+
afterSeparator = true
|
|
291
|
+
continue
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (token.startsWith('--')) {
|
|
295
|
+
// `--flag=value` carries its value inline
|
|
296
|
+
let equalsIndex = token.indexOf('=')
|
|
297
|
+
let flag = equalsIndex === -1 ? token : token.slice(0, equalsIndex)
|
|
298
|
+
|
|
299
|
+
if (TEST_VALUE_FLAG_SET.has(flag)) {
|
|
300
|
+
usedFlags.add(flag)
|
|
301
|
+
if (equalsIndex === -1) {
|
|
302
|
+
expectedValueFor = flag
|
|
303
|
+
}
|
|
304
|
+
continue
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (TEST_BOOLEAN_FLAG_SET.has(flag)) {
|
|
308
|
+
usedFlags.add(flag)
|
|
309
|
+
continue
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return completeValues([], currentWord)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (token.startsWith('-') && token !== '-') {
|
|
316
|
+
let aliased = TEST_FLAG_ALIASES.get(token.slice(0, 2))
|
|
317
|
+
|
|
318
|
+
// A value alias may carry its value inline (`-c1`); a bare alias
|
|
319
|
+
// expects the value in the next token.
|
|
320
|
+
if (aliased !== undefined && TEST_VALUE_FLAG_SET.has(aliased)) {
|
|
321
|
+
usedFlags.add(aliased)
|
|
322
|
+
if (token.length === 2) {
|
|
323
|
+
expectedValueFor = aliased
|
|
324
|
+
}
|
|
325
|
+
continue
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// A group of boolean aliases (`-qw`)
|
|
329
|
+
let group = [...token.slice(1)].map((char) => TEST_FLAG_ALIASES.get(`-${char}`))
|
|
330
|
+
if (
|
|
331
|
+
group.every((flag): flag is string => flag !== undefined && TEST_BOOLEAN_FLAG_SET.has(flag))
|
|
332
|
+
) {
|
|
333
|
+
for (let flag of group) {
|
|
334
|
+
usedFlags.add(flag)
|
|
335
|
+
}
|
|
336
|
+
continue
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return completeValues([], currentWord)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Anything else is a positional test file glob
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (expectedValueFor != null) {
|
|
346
|
+
return TEST_FILE_VALUE_FLAGS.has(expectedValueFor) ? { mode: 'files' } : { mode: 'none' }
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (afterSeparator) {
|
|
350
|
+
return { mode: 'files' }
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
let longFlags = [...TEST_BOOLEAN_FLAGS, ...TEST_VALUE_FLAGS].filter(
|
|
354
|
+
(flag) => !usedFlags.has(flag) || TEST_REPEATABLE_FLAGS.has(flag),
|
|
355
|
+
)
|
|
356
|
+
let shortFlags = [...TEST_FLAG_ALIASES].flatMap(([short, long]) =>
|
|
357
|
+
!usedFlags.has(long) || TEST_REPEATABLE_FLAGS.has(long) ? [short] : [],
|
|
358
|
+
)
|
|
359
|
+
let suggestions = withHelpFlags([...longFlags, ...shortFlags], usedGlobalFlags)
|
|
360
|
+
|
|
361
|
+
return currentWord.startsWith('-') || currentWord === ''
|
|
362
|
+
? completeValues(suggestions, currentWord)
|
|
363
|
+
: { mode: 'files' }
|
|
364
|
+
}
|
|
365
|
+
|
|
182
366
|
function completeHelp(
|
|
183
367
|
tokens: string[],
|
|
184
368
|
currentWord: string,
|
|
@@ -313,6 +497,105 @@ function completeRoutes(
|
|
|
313
497
|
return completeValues(flags, currentWord)
|
|
314
498
|
}
|
|
315
499
|
|
|
500
|
+
function completeDb(
|
|
501
|
+
tokens: string[],
|
|
502
|
+
currentWord: string,
|
|
503
|
+
usedGlobalFlags: Set<string>,
|
|
504
|
+
): CompletionResult {
|
|
505
|
+
let filteredTokens = filterGlobalCommandTokens(tokens, usedGlobalFlags)
|
|
506
|
+
if (filteredTokens == null) {
|
|
507
|
+
return completeValues([], currentWord)
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (filteredTokens.length === 0) {
|
|
511
|
+
return completeValues(withHelpFlags([...DB_COMMANDS], usedGlobalFlags), currentWord)
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
let [subcommand, ...rest] = filteredTokens
|
|
515
|
+
|
|
516
|
+
if (subcommand === 'migrate') {
|
|
517
|
+
return completeDbOptions(
|
|
518
|
+
rest,
|
|
519
|
+
currentWord,
|
|
520
|
+
usedGlobalFlags,
|
|
521
|
+
[],
|
|
522
|
+
['--connection-env', '--journal-table', '--migrations', '--to'],
|
|
523
|
+
)
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (subcommand === 'reset') {
|
|
527
|
+
return completeDbOptions(
|
|
528
|
+
rest,
|
|
529
|
+
currentWord,
|
|
530
|
+
usedGlobalFlags,
|
|
531
|
+
['--force'],
|
|
532
|
+
['--connection-env', '--journal-table', '--migrations', '--seed'],
|
|
533
|
+
)
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (subcommand === 'wipe') {
|
|
537
|
+
return completeDbOptions(rest, currentWord, usedGlobalFlags, ['--force'], ['--connection-env'])
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (subcommand === 'seed') {
|
|
541
|
+
return completeDbOptions(rest, currentWord, usedGlobalFlags, [], ['--connection-env', '--seed'])
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (subcommand === 'status') {
|
|
545
|
+
return completeDbOptions(
|
|
546
|
+
rest,
|
|
547
|
+
currentWord,
|
|
548
|
+
usedGlobalFlags,
|
|
549
|
+
[],
|
|
550
|
+
['--connection-env', '--journal-table', '--migrations'],
|
|
551
|
+
)
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return completeValues([], currentWord)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function completeDbOptions(
|
|
558
|
+
tokens: string[],
|
|
559
|
+
currentWord: string,
|
|
560
|
+
usedGlobalFlags: Set<string>,
|
|
561
|
+
booleanFlags: string[],
|
|
562
|
+
valueFlags: string[],
|
|
563
|
+
): CompletionResult {
|
|
564
|
+
let usedFlags = new Set<string>()
|
|
565
|
+
let expectedValueFor: string | undefined
|
|
566
|
+
|
|
567
|
+
for (let token of tokens) {
|
|
568
|
+
if (expectedValueFor !== undefined) {
|
|
569
|
+
expectedValueFor = undefined
|
|
570
|
+
continue
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
let equalsIndex = token.indexOf('=')
|
|
574
|
+
let flag = equalsIndex === -1 ? token : token.slice(0, equalsIndex)
|
|
575
|
+
if (booleanFlags.includes(flag)) {
|
|
576
|
+
usedFlags.add(flag)
|
|
577
|
+
continue
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (valueFlags.includes(flag)) {
|
|
581
|
+
usedFlags.add(flag)
|
|
582
|
+
if (equalsIndex === -1) expectedValueFor = flag
|
|
583
|
+
continue
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return completeValues([], currentWord)
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (expectedValueFor !== undefined) {
|
|
590
|
+
return expectedValueFor === '--migrations' || expectedValueFor === '--seed'
|
|
591
|
+
? { mode: 'files' }
|
|
592
|
+
: { mode: 'none' }
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
let available = [...booleanFlags, ...valueFlags].filter((flag) => !usedFlags.has(flag))
|
|
596
|
+
return completeValues(withHelpFlags(available, usedGlobalFlags), currentWord)
|
|
597
|
+
}
|
|
598
|
+
|
|
316
599
|
function completeCompletionCommand(
|
|
317
600
|
tokens: string[],
|
|
318
601
|
currentWord: string,
|
|
@@ -356,6 +639,7 @@ function completeSimpleFlags(
|
|
|
356
639
|
|
|
357
640
|
function getTopLevelSuggestions(currentWord: string, usedFlags: Set<string>): string[] {
|
|
358
641
|
let flags = [
|
|
642
|
+
...(!usedFlags.has('--config') ? ['--config'] : []),
|
|
359
643
|
...(!usedFlags.has('-h') ? ['-h', '--help'] : []),
|
|
360
644
|
...(!usedFlags.has('--no-color') ? ['--no-color'] : []),
|
|
361
645
|
...(!usedFlags.has('-v') ? ['-v', '--version'] : []),
|
|
@@ -371,6 +655,7 @@ function getTopLevelSuggestions(currentWord: string, usedFlags: Set<string>): st
|
|
|
371
655
|
function withHelpFlags(values: string[], usedGlobalFlags: Set<string>): string[] {
|
|
372
656
|
return [
|
|
373
657
|
...values,
|
|
658
|
+
...(!usedGlobalFlags.has('--config') ? ['--config'] : []),
|
|
374
659
|
...(!usedGlobalFlags.has('-h') ? ['-h', '--help'] : []),
|
|
375
660
|
...(!usedGlobalFlags.has('--no-color') ? ['--no-color'] : []),
|
|
376
661
|
]
|
|
@@ -382,7 +667,20 @@ function filterGlobalCommandTokens(
|
|
|
382
667
|
): string[] | null {
|
|
383
668
|
let filtered: string[] = []
|
|
384
669
|
|
|
385
|
-
for (let
|
|
670
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
671
|
+
let token = tokens[index]!
|
|
672
|
+
|
|
673
|
+
if (token === '--config') {
|
|
674
|
+
usedGlobalFlags.add('--config')
|
|
675
|
+
index++
|
|
676
|
+
continue
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (token.startsWith('--config=')) {
|
|
680
|
+
usedGlobalFlags.add('--config')
|
|
681
|
+
continue
|
|
682
|
+
}
|
|
683
|
+
|
|
386
684
|
if (token === '--no-color') {
|
|
387
685
|
usedGlobalFlags.add('--no-color')
|
|
388
686
|
continue
|
|
@@ -399,6 +697,23 @@ function filterGlobalCommandTokens(
|
|
|
399
697
|
return filtered
|
|
400
698
|
}
|
|
401
699
|
|
|
700
|
+
function expectsGlobalConfigValue(tokens: string[]): boolean {
|
|
701
|
+
let expectsValue = false
|
|
702
|
+
|
|
703
|
+
for (let token of tokens) {
|
|
704
|
+
if (expectsValue) {
|
|
705
|
+
expectsValue = false
|
|
706
|
+
continue
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
if (token === '--config') {
|
|
710
|
+
expectsValue = true
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return expectsValue
|
|
715
|
+
}
|
|
716
|
+
|
|
402
717
|
function completeValues(values: string[], currentWord: string): CompletionResult {
|
|
403
718
|
return {
|
|
404
719
|
mode: 'values',
|
|
@@ -3,8 +3,6 @@ import * as path from 'node:path'
|
|
|
3
3
|
const OWNER_FILE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'] as const
|
|
4
4
|
const ACTIONS_DIRECTORY = path.join('app', 'actions')
|
|
5
5
|
|
|
6
|
-
export type OwnerFileExtension = (typeof OWNER_FILE_EXTENSIONS)[number]
|
|
7
|
-
|
|
8
6
|
export function getControllerOwnerCandidates(segments: string[]): string[] {
|
|
9
7
|
return OWNER_FILE_EXTENSIONS.map((extension) =>
|
|
10
8
|
normalizeRelativePath(path.join(ACTIONS_DIRECTORY, ...segments, `controller${extension}`)),
|
|
@@ -20,17 +18,6 @@ export function getPreferredOwnerDisplayPath(candidates: string[]): string {
|
|
|
20
18
|
return tsxCandidate ?? candidates[0] ?? ''
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
export function getOwnerCandidateForExtension(
|
|
24
|
-
candidates: string[],
|
|
25
|
-
extension: OwnerFileExtension,
|
|
26
|
-
): string | null {
|
|
27
|
-
return candidates.find((candidate) => candidate.endsWith(extension)) ?? null
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function getOwnerFileExtension(filePath: string): OwnerFileExtension | null {
|
|
31
|
-
return OWNER_FILE_EXTENSIONS.find((extension) => filePath.endsWith(extension)) ?? null
|
|
32
|
-
}
|
|
33
|
-
|
|
34
21
|
export function toDiskSegment(segment: string): string {
|
|
35
22
|
let diskSegment = segment
|
|
36
23
|
.replace(/[\\/]+/g, '-')
|
|
@@ -45,31 +32,6 @@ export function toDiskSegment(segment: string): string {
|
|
|
45
32
|
return diskSegment.length === 0 ? 'route' : diskSegment
|
|
46
33
|
}
|
|
47
34
|
|
|
48
|
-
export function isControllerEntryFileName(fileName: string): boolean {
|
|
49
|
-
return OWNER_FILE_EXTENSIONS.some((extension) => fileName === `controller${extension}`)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function getOwnerModuleBaseName(fileName: string): string | null {
|
|
53
|
-
let extension = OWNER_FILE_EXTENSIONS.find((ownerExtension) => fileName.endsWith(ownerExtension))
|
|
54
|
-
|
|
55
|
-
if (extension == null) {
|
|
56
|
-
return null
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return fileName.slice(0, -extension.length)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function isActionFileName(fileName: string): boolean {
|
|
63
|
-
let baseName = getOwnerModuleBaseName(fileName)
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
baseName != null &&
|
|
67
|
-
baseName !== 'controller' &&
|
|
68
|
-
!baseName.endsWith('.test') &&
|
|
69
|
-
!baseName.endsWith('.spec')
|
|
70
|
-
)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
35
|
function normalizeRelativePath(filePath: string): string {
|
|
74
36
|
return filePath.split(path.sep).join('/')
|
|
75
37
|
}
|