Skip to content

Commit c6b28c9

Browse files
committed
feat: similar cmd suggestions
1 parent 62a3c40 commit c6b28c9

9 files changed

+88
-45
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.7.5
2+
3+
- Feat: Command suggestions by prefix
4+
15
## 0.7.4
26

37
- Fix: Script ENV not being passed properly

bin/script_runner.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'dart:io' as io;
22

3-
import 'package:script_runner/src/base.dart';
4-
import 'package:script_runner/src/utils.dart';
3+
import 'package:script_runner/base.dart';
4+
import 'package:script_runner/utils.dart';
55

66
/// Main entrypoint for CMD script runner.
77
Future<void> main(List<String> args) async {
@@ -15,7 +15,11 @@ Future<void> main(List<String> args) async {
1515
final code = await runScript(scriptCmd, scriptArgs);
1616
io.exit(code);
1717
} catch (e, stack) {
18-
printColor('$e\n$stack', [TerminalColor.red]);
18+
if (e is ScriptStateError) {
19+
printColor(e.toString(), [TerminalColor.red]);
20+
} else {
21+
printColor('$e\n$stack', [TerminalColor.red]);
22+
}
1923
if (e is io.ProcessException) {
2024
io.exit(e.errorCode);
2125
}

lib/base.dart

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import 'config.dart';
2+
import 'utils.dart';
3+
4+
/// Runs a script with the given name, and any extra arguments.
5+
/// Returns the exit code.
6+
Future<int> runScript(String entryName, List<String> args) async {
7+
final config = await ScriptRunnerConfig.get();
8+
9+
if (config.scripts.isEmpty) {
10+
throw ScriptStateError('No scripts found');
11+
}
12+
13+
if (['-h', '--help'].contains(entryName)) {
14+
config.printUsage();
15+
return 0;
16+
}
17+
18+
if (['-ls', '--list'].contains(entryName)) {
19+
final search = args.isNotEmpty ? args.first : '';
20+
config.printScripts(search);
21+
return 0;
22+
}
23+
24+
final entry = config.scriptsMap[entryName];
25+
26+
if (entry == null) {
27+
final suggestions = config.scriptsMap.keys
28+
.where((key) => key.toLowerCase().startsWith(entryName.toLowerCase()))
29+
.toList();
30+
31+
if (suggestions.isNotEmpty) {
32+
if (suggestions.length == 1) {
33+
throw ScriptStateError(
34+
'No script named "$entryName" found. Did you mean "${suggestions.single}"?',
35+
);
36+
} else {
37+
throw ScriptStateError(
38+
'No script named "$entryName" found.\n'
39+
'Did you mean one of: "${suggestions.join('", "')}"?',
40+
);
41+
}
42+
} else {
43+
throw ScriptStateError(
44+
'No script named "$entryName" found.\n'
45+
'Available scripts: ${config.scriptsMap.keys.join('", "')}',
46+
);
47+
}
48+
}
49+
50+
return entry.run(args);
51+
}
52+

lib/src/config.dart renamed to lib/config.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class ScriptRunnerConfig {
7575
final sourceMap = await _tryFindConfig(fs, startDir);
7676

7777
if (sourceMap.isEmpty) {
78-
throw StateError('Must provide scripts in either pubspec.yaml or script_runner.yaml');
78+
throw ScriptStateError('Must provide scripts in either pubspec.yaml or script_runner.yaml');
7979
}
8080

8181
final source = sourceMap.values.first;
@@ -235,7 +235,7 @@ class ScriptRunnerShellConfig {
235235
});
236236

237237
/// Parses a shell configuration from a [YamlMap], [Map] or [String].
238-
/// Other types will throw a [StateError].
238+
/// Other types will throw a [ScriptStateError].
239239
factory ScriptRunnerShellConfig.parse(dynamic obj) {
240240
try {
241241
if (obj is String) {
@@ -252,9 +252,9 @@ class ScriptRunnerShellConfig {
252252
if (obj == null) {
253253
return ScriptRunnerShellConfig();
254254
}
255-
throw StateError('Invalid shell config: $obj');
255+
throw ScriptStateError('Invalid shell config: $obj');
256256
} catch (e) {
257-
throw StateError('Error while parsing config: $obj');
257+
throw ScriptStateError('Error while parsing config: $obj');
258258
}
259259
}
260260

@@ -295,7 +295,7 @@ class ScriptRunnerShellConfig {
295295
} else if (Platform.isLinux) {
296296
return OS.linux;
297297
}
298-
throw StateError('Unsupported OS: ${Platform.operatingSystem}');
298+
throw ScriptStateError('Unsupported OS: ${Platform.operatingSystem}');
299299
// return OS.unknown;
300300
}
301301

lib/src/runnable_script.dart renamed to lib/runnable_script.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import 'dart:io' as io;
33
import 'package:file/file.dart';
44
import 'package:file/local.dart';
55
import 'package:file/memory.dart';
6-
import 'package:script_runner/src/config.dart';
6+
7+
import 'config.dart';
78
// ignore: no_leading_underscores_for_library_prefixes
8-
import 'package:script_runner/src/utils.dart' as _utils;
9+
import 'utils.dart' as _utils;
10+
import 'utils.dart';
911

1012
/// A runnable script with pre-defined name, cmd and args. May be run using the `run` command and optionally
1113
/// supplying extra arguments to pass.
@@ -98,7 +100,7 @@ class RunnableScript {
98100
env: map['env'] as Map<String, String>? ?? {},
99101
);
100102
} catch (e) {
101-
throw StateError('Failed to parse script, arguments: $map, $fileSystem. Error: $e');
103+
throw ScriptStateError('Failed to parse script, arguments: $map, $fileSystem. Error: $e');
102104
}
103105
}
104106

lib/script_runner.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/// This is the primary library used for loading and running scripts.
22
library script_runner;
33

4-
export 'src/base.dart' show runScript;
5-
export 'src/config.dart' show ScriptRunnerConfig, ScriptRunnerShellConfig;
6-
export 'src/runnable_script.dart' show RunnableScript;
4+
export 'base.dart' show runScript;
5+
export 'config.dart' show ScriptRunnerConfig, ScriptRunnerShellConfig;
6+
export 'runnable_script.dart' show RunnableScript;

lib/src/base.dart

Lines changed: 0 additions & 28 deletions
This file was deleted.

lib/src/utils.dart renamed to lib/utils.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,10 @@ class TerminalColor {
134134
static const TerminalColor bold = TerminalColor._(1);
135135
static const TerminalColor underline = TerminalColor._(4);
136136
}
137+
138+
class ScriptStateError extends StateError {
139+
ScriptStateError(super.message);
140+
141+
@override
142+
String toString() => message;
143+
}

pubspec.yaml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: script_runner
22
description: Run all your project-related scripts in a portable, simple config.
3-
version: 0.7.4
3+
version: 0.7.5
44
homepage: https://casraf.dev/projects/dart-script-runner
55
repository: https://github.com/chenasraf/dart_script_runner
66
license: MIT
@@ -45,10 +45,12 @@ script_runner:
4545
- 'Hello World'
4646
description: test script foobar
4747
display_cmd: false
48+
- name: clean
49+
cmd: rm -rf .dart_tool/pub/bin/script_runner/script_runner.dart-*.snapshot
4850
- name: activate-local
49-
cmd: dart pub global activate --source path ./
51+
cmd: scr clean && dart pub global deactivate script_runner; dart pub global activate --source path ./
5052
- name: activate-global
51-
cmd: dart pub global activate script_runner
53+
cmd: scr clean && dart pub global deactivate script_runner; dart pub global activate script_runner
5254
- name: combined
5355
cmd: echo 'test' && echo1 && echo2
5456
- short: echo 'this is a short script'

0 commit comments

Comments
 (0)