From 58b7e86fe4fe6a40d829970582597793c1400238 Mon Sep 17 00:00:00 2001 From: LeadcodeDev Date: Tue, 25 Apr 2023 00:49:39 +0200 Subject: [PATCH 1/6] feat: Implement unified console --- lib/src/internal/mixins/console.dart | 6 ++-- .../services/console/console_service.dart | 30 +++++++++++++++++++ .../console/themes/console_theme.dart | 14 +++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 lib/src/internal/services/console/console_service.dart create mode 100644 lib/src/internal/services/console/themes/console_theme.dart diff --git a/lib/src/internal/mixins/console.dart b/lib/src/internal/mixins/console.dart index cb4201684..020cbbb91 100644 --- a/lib/src/internal/mixins/console.dart +++ b/lib/src/internal/mixins/console.dart @@ -1,7 +1,7 @@ -import 'package:mineral_cli/mineral_cli.dart'; -import 'package:mineral_console/mineral_console.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; +import 'package:mineral_contract/mineral_contract.dart'; import 'package:mineral_ioc/ioc.dart'; mixin Console { - ConsoleContract get console => ioc.use().console; + ConsoleServiceContract get console => ioc.use(); } \ No newline at end of file diff --git a/lib/src/internal/services/console/console_service.dart b/lib/src/internal/services/console/console_service.dart new file mode 100644 index 000000000..73ba6a645 --- /dev/null +++ b/lib/src/internal/services/console/console_service.dart @@ -0,0 +1,30 @@ +import 'dart:io'; + +import 'package:mineral/src/internal/services/console/contracts/theme_contract.dart'; +import 'package:mineral_contract/mineral_contract.dart'; + +class ConsoleService extends ConsoleServiceContract { + final ThemeContract theme; + + ConsoleService({ required this.theme }); + + @override + void info (String message) { + stdout.writeln('${theme.infoPrefix} $message'); + } + + @override + void success (String message) { + stdout.writeln('${theme.successPrefix} $message'); + } + + @override + void warn (String message) { + stdout.writeln('${theme.warnPrefix} $message'); + } + + @override + void error (String message) { + stdout.writeln('${theme.errorPrefix} $message'); + } +} \ No newline at end of file diff --git a/lib/src/internal/services/console/themes/console_theme.dart b/lib/src/internal/services/console/themes/console_theme.dart new file mode 100644 index 000000000..9f1653212 --- /dev/null +++ b/lib/src/internal/services/console/themes/console_theme.dart @@ -0,0 +1,14 @@ +import 'package:mineral/src/internal/services/console/theme.dart'; +import 'package:tint/tint.dart'; + +class ConsoleTheme extends Theme { + ConsoleTheme(): super( + inputPrefix: '?'.padRight(2).yellow(), + successPrefix: '[ ' + 'success'.padRight(2).green() + ' ]', + errorPrefix: '[ error ]'.padRight(2).red(), + booleanPrefix: '(y/n)'.padLeft(2).grey(), + pickedItemPrefix: '❯'.green(), + infoPrefix: '[ ' + 'success'.padRight(2).cyan() + ' ]', + warnPrefix: '[ warn ]'.yellow(), + ); +} \ No newline at end of file From 6f0f303a3fe358d91fe27668adec5b46844e2224 Mon Sep 17 00:00:00 2001 From: LeadcodeDev Date: Tue, 25 Apr 2023 00:50:29 +0200 Subject: [PATCH 2/6] feat: Implement new cli service --- lib/cli.dart | 3 + .../api/interactions/command_interaction.dart | 1 - .../services/cli/command_line_interface.dart | 79 +++++++++++++++++++ .../services/cli/entities/cli_command.dart | 35 ++++++++ .../cli/themes/theme.dart} | 3 +- lib/src/internal/services/console/ansi.dart | 16 ++++ lib/src/internal/services/console/theme.dart | 36 +++++++++ 7 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 lib/cli.dart create mode 100644 lib/src/internal/services/cli/command_line_interface.dart create mode 100644 lib/src/internal/services/cli/entities/cli_command.dart rename lib/src/internal/{themes/mineral_theme.dart => services/cli/themes/theme.dart} (79%) create mode 100644 lib/src/internal/services/console/ansi.dart create mode 100644 lib/src/internal/services/console/theme.dart diff --git a/lib/cli.dart b/lib/cli.dart new file mode 100644 index 000000000..82a800905 --- /dev/null +++ b/lib/cli.dart @@ -0,0 +1,3 @@ +library cli; + +export 'src/internal/services/cli/command_line_interface.dart' show CommandLineInterface; \ No newline at end of file diff --git a/lib/src/api/interactions/command_interaction.dart b/lib/src/api/interactions/command_interaction.dart index c5e327265..51c53eb80 100644 --- a/lib/src/api/interactions/command_interaction.dart +++ b/lib/src/api/interactions/command_interaction.dart @@ -2,7 +2,6 @@ import 'dart:core'; import 'package:mineral/core/api.dart'; import 'package:mineral/framework.dart'; -import 'package:mineral_cli/mineral_cli.dart'; import 'package:mineral_ioc/ioc.dart'; class CommandInteraction extends Interaction { diff --git a/lib/src/internal/services/cli/command_line_interface.dart b/lib/src/internal/services/cli/command_line_interface.dart new file mode 100644 index 000000000..f9ea6bcac --- /dev/null +++ b/lib/src/internal/services/cli/command_line_interface.dart @@ -0,0 +1,79 @@ +import 'package:args/args.dart'; +import 'package:mineral/src/commands/compile/exe.dart'; +import 'package:mineral/src/commands/compile/js.dart'; +import 'package:mineral/src/commands/help.dart'; +import 'package:mineral/src/commands/make/command.dart'; +import 'package:mineral/src/commands/make/event.dart'; +import 'package:mineral/src/commands/make/package.dart'; +import 'package:mineral/src/commands/make/service.dart'; +import 'package:mineral/src/commands/make/shared_state.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; +import 'package:mineral/src/internal/services/console/themes/console_theme.dart'; +import 'package:mineral/src/internal/services/console/themes/default_theme.dart'; +import 'package:mineral_contract/mineral_contract.dart'; +import 'package:mineral_ioc/ioc.dart'; + +class CommandLineInterface extends CliServiceContract { + final List packages; + final ArgParser _parser = ArgParser(); + final Map _commands = {}; + final ConsoleService _console = ConsoleService(theme: DefaultTheme()); + + CommandLineInterface({ this.packages = const [] }) { + ioc.bind((ioc) => ConsoleService(theme: ConsoleTheme())); + + register([ + MakeEvent(_console), + MakeCommand(_console), + MakeSharedState(_console), + MakeModule(_console), + MakeService(_console), + CompileExecutable(_console), + CompileJavascript(_console), + Help(_console, _commands), + ]); + + for (final package in packages) { + register(package.injectCommands()); + } + } + + @override + ConsoleService get console => _console; + + @override + void register (List commands) { + for (final command in commands) { + _commands.putIfAbsent(command.name, () => command); + + final ArgParser parser = ArgParser(); + if (command.arguments.isNotEmpty) { + for (final argument in command.arguments) { + parser.addOption(argument); + } + } + + _parser.addCommand(command.name, parser); + } + } + + @override + Future handle (List arguments) async { + ArgResults results = _parser.parse(arguments); + final command = _commands[results.command?.name ?? 'help']; + + if (command != null) { + if (command.arguments.isNotEmpty && results.arguments.length - 1 != command.arguments.length) { + command.console.error('Please provide ${command.arguments.map((e) => '<$e>').join(', ')} params.'); + return; + } + + final params = {}; + for (int i = 0; i < command.arguments.length; i++) { + params.putIfAbsent(command.arguments[i], () => results.arguments[i + 1]); + } + + return await command.handle(params); + } + } +} \ No newline at end of file diff --git a/lib/src/internal/services/cli/entities/cli_command.dart b/lib/src/internal/services/cli/entities/cli_command.dart new file mode 100644 index 000000000..1d83ab2b3 --- /dev/null +++ b/lib/src/internal/services/cli/entities/cli_command.dart @@ -0,0 +1,35 @@ +import 'dart:io'; + +import 'package:mineral/src/internal/services/console/console_service.dart'; +import 'package:path/path.dart'; + +abstract class CliCommand { + final ConsoleService _console; + final String _name; + final String _description; + final List _args; + + CliCommand(this._console, this._name, this._description, this._args); + + ConsoleService get console => _console; + String get name => _name; + String get description => _description; + List get arguments => _args; + + Future handle (Map args); + + Future> getDirectories (String location) async { + List directories = []; + directories.add(Directory(join(Directory.current.path, location))); + + Stream files = Directory(join(Directory.current.path, location)).list(recursive: true); + + await files.forEach((element) { + if (element is Directory) { + directories.add(element); + } + }); + + return directories; + } +} diff --git a/lib/src/internal/themes/mineral_theme.dart b/lib/src/internal/services/cli/themes/theme.dart similarity index 79% rename from lib/src/internal/themes/mineral_theme.dart rename to lib/src/internal/services/cli/themes/theme.dart index 4c9ba80d3..9ba930f87 100644 --- a/lib/src/internal/themes/mineral_theme.dart +++ b/lib/src/internal/services/cli/themes/theme.dart @@ -1,4 +1,5 @@ -import 'package:mineral_console/mineral_console.dart'; +import 'package:mineral/src/internal/services/console/theme.dart'; +import 'package:tint/tint.dart'; class MineralTheme extends Theme { MineralTheme(): super( diff --git a/lib/src/internal/services/console/ansi.dart b/lib/src/internal/services/console/ansi.dart new file mode 100644 index 000000000..a95cdbf5a --- /dev/null +++ b/lib/src/internal/services/console/ansi.dart @@ -0,0 +1,16 @@ +const ansiDeviceStatusReportCursorPosition = '\x1b[6n'; +const ansiEraseInDisplayAll = '\x1b[2J'; +const ansiEraseInLineAll = '\x1b[2K'; +const ansiEraseCursorToEnd = '\x1b[K'; + +const ansiHideCursor = '\x1b[?25l'; +const ansiShowCursor = '\x1b[?25h'; + +const ansiCursorLeft = '\x1b[D'; +const ansiCursorRight = '\x1b[C'; +const ansiCursorUp = '\x1b[A'; +const ansiCursorDown = '\x1b[B'; + +const ansiResetCursorPosition = '\x1b[H'; +const ansiMoveCursorToScreenEdge = '\x1b[999C\x1b[999B'; +String ansiCursorPosition(int row, int col) => '\x1b[$row;${col}H'; \ No newline at end of file diff --git a/lib/src/internal/services/console/theme.dart b/lib/src/internal/services/console/theme.dart new file mode 100644 index 000000000..f476198e9 --- /dev/null +++ b/lib/src/internal/services/console/theme.dart @@ -0,0 +1,36 @@ + + +import 'package:mineral/src/internal/services/console/contracts/theme_contract.dart'; + +class Theme implements ThemeContract { + @override + final String inputPrefix; + + @override + final String successPrefix; + + @override + final String errorPrefix; + + @override + final String booleanPrefix; + + @override + final String pickedItemPrefix; + + @override + final String infoPrefix; + + @override + final String warnPrefix; + + const Theme({ + required this.inputPrefix, + required this.successPrefix, + required this.errorPrefix, + required this.booleanPrefix, + required this.pickedItemPrefix, + required this.infoPrefix, + required this.warnPrefix, + }); +} \ No newline at end of file From fe58fdc739afd775c4ea85c585b8122335ea9d5f Mon Sep 17 00:00:00 2001 From: LeadcodeDev Date: Tue, 25 Apr 2023 00:53:22 +0200 Subject: [PATCH 3/6] feat: Improve commands --- lib/src/commands/compile/exe.dart | 8 ++-- lib/src/commands/compile/js.dart | 8 ++-- lib/src/commands/help.dart | 19 ++++----- lib/src/commands/make/event.dart | 13 ++++--- .../make/{module.dart => package.dart} | 13 ++++--- lib/src/commands/make/service.dart | 13 ++++--- lib/src/commands/make/shared_state.dart | 13 ++++--- .../services/cli/command_line_interface.dart | 2 +- .../internal/services/console/component.dart | 39 +++++++++++++++++++ 9 files changed, 86 insertions(+), 42 deletions(-) rename lib/src/commands/make/{module.dart => package.dart} (73%) create mode 100644 lib/src/internal/services/console/component.dart diff --git a/lib/src/commands/compile/exe.dart b/lib/src/commands/compile/exe.dart index e8524ab8d..d5f85659b 100644 --- a/lib/src/commands/compile/exe.dart +++ b/lib/src/commands/compile/exe.dart @@ -1,11 +1,11 @@ import 'dart:io'; -import 'package:mineral_cli/mineral_cli.dart'; -import 'package:mineral_console/mineral_console.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; +import 'package:mineral_contract/mineral_contract.dart'; import 'package:path/path.dart'; -class CompileExecutable extends CliCommand { - CompileExecutable(Console console): super(console, 'compile:exe', 'Compile your application to an executable file', []); +class CompileExecutable extends CliCommandContract { + CompileExecutable(ConsoleService console): super(console, 'compile:exe', 'Compile your application to an executable file', []); @override Future handle(args) async { diff --git a/lib/src/commands/compile/js.dart b/lib/src/commands/compile/js.dart index d7d0c1c26..ffd962c23 100644 --- a/lib/src/commands/compile/js.dart +++ b/lib/src/commands/compile/js.dart @@ -1,11 +1,11 @@ import 'dart:io'; -import 'package:mineral_cli/mineral_cli.dart'; -import 'package:mineral_console/mineral_console.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; +import 'package:mineral_contract/mineral_contract.dart'; import 'package:path/path.dart'; -class CompileJavascript extends CliCommand { - CompileJavascript(Console console): super(console, 'compile:js', 'Compile your application to an javascript file', []); +class CompileJavascript extends CliCommandContract { + CompileJavascript(ConsoleService console): super(console, 'compile:js', 'Compile your application to an javascript file', []); @override Future handle(args) async { diff --git a/lib/src/commands/help.dart b/lib/src/commands/help.dart index d8594f7ab..ce2325916 100644 --- a/lib/src/commands/help.dart +++ b/lib/src/commands/help.dart @@ -1,18 +1,19 @@ import 'dart:io'; +import 'package:mineral_contract/mineral_contract.dart'; +import 'package:tint/tint.dart'; import 'package:collection/collection.dart'; -import 'package:mineral_cli/mineral_cli.dart'; -import 'package:mineral_console/mineral_console.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; -class Help extends CliCommand { - final MineralCliContract _cli; - Help(Console console, this._cli): super(console, 'help', 'Displays the list of commands', []); +class Help extends CliCommandContract { + final Map _commands; + Help(ConsoleService console, this._commands): super(console, 'help', 'Displays the list of commands', []); @override Future handle(args) async { - Map> commands = {}; + Map> commands = {}; - for (final command in _cli.commands.values) { + for (final command in _commands.values) { final String key = command.name.contains(':') ? command.name.split(':').first : 'Available commands'; @@ -25,8 +26,8 @@ class Help extends CliCommand { } String display = ''; - _cli.commands.values.toList().sort((a, b) => a.arguments.length + b.arguments.length); - int maxArgumentLength = _cli.commands.values.last.arguments.map((argument) => '<$argument>').join('').length; + _commands.values.toList().sort((a, b) => a.arguments.length + b.arguments.length); + int maxArgumentLength = _commands.values.last.arguments.map((argument) => '<$argument>').join('').length; for (final group in commands.entries.sorted((a, b) => b.key.length.compareTo(a.key.length))) { display += '${group.key.blue()}\n'; diff --git a/lib/src/commands/make/event.dart b/lib/src/commands/make/event.dart index 351c41500..ab0c6064f 100644 --- a/lib/src/commands/make/event.dart +++ b/lib/src/commands/make/event.dart @@ -1,17 +1,18 @@ import 'dart:io'; +import 'package:mineral_contract/mineral_contract.dart'; +import 'package:prompts/prompts.dart' as prompts; import 'package:mineral/src/commands/templates/event.dart'; -import 'package:mineral_cli/mineral_cli.dart'; -import 'package:mineral_console/mineral_console.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; import 'package:path/path.dart'; import 'package:recase/recase.dart'; -class MakeEvent extends CliCommand { - MakeEvent(Console console): super(console, 'make:event', 'Create new event file', ['name']); +class MakeEvent extends CliCommandContract { + MakeEvent(ConsoleService console): super(console, 'make:event', 'Create new event file', ['name']); @override Future handle(Map args) async { - final placeInRootFolder = Confirm('Would you like to change the location of your ?', defaultValue: false).build(); + final placeInRootFolder = prompts.getBool('Would you like to change the location of your ?', defaultsTo: false); if (!placeInRootFolder) { final file = File(join(Directory.current.path, 'src', '${ReCase(args['name']).snakeCase}.dart')); @@ -19,7 +20,7 @@ class MakeEvent extends CliCommand { return; } - final location = Ask('Where do you want to create your event file ?').build(); + final location = prompts.get('Where do you want to create your event file ?'); final file = File(join(Directory.current.path, 'src', location, '${ReCase(args['name']).snakeCase}.dart')); await _createFile(file, args); } diff --git a/lib/src/commands/make/module.dart b/lib/src/commands/make/package.dart similarity index 73% rename from lib/src/commands/make/module.dart rename to lib/src/commands/make/package.dart index e53653098..0e8c477f0 100644 --- a/lib/src/commands/make/module.dart +++ b/lib/src/commands/make/package.dart @@ -1,17 +1,18 @@ import 'dart:io'; +import 'package:mineral/src/internal/services/console/console_service.dart'; +import 'package:mineral_contract/mineral_contract.dart'; +import 'package:prompts/prompts.dart' as prompts; import 'package:mineral/src/commands/templates/module.dart'; -import 'package:mineral_cli/mineral_cli.dart'; -import 'package:mineral_console/mineral_console.dart'; import 'package:path/path.dart'; import 'package:recase/recase.dart'; -class MakeModule extends CliCommand { - MakeModule(Console console): super(console, 'make:module', 'Create new module structure', ['name']); +class MakePackage extends CliCommandContract { + MakePackage(ConsoleService console): super(console, 'make:package', 'Create new package structure', ['name']); @override Future handle(Map args) async { - final placeInRootFolder = Confirm('Would you like to change the location of your ?', defaultValue: false).build(); + final placeInRootFolder = prompts.getBool('Would you like to change the location of your ?', defaultsTo: false); final Directory directory = Directory(join(Directory.current.path, 'src', ReCase(args['name']).snakeCase)); if (!placeInRootFolder) { @@ -20,7 +21,7 @@ class MakeModule extends CliCommand { return; } - final location = Ask('Where do you want to create your command file ?').build(); + final location = prompts.get('Where do you want to create your package ?'); final file = File(join(directory.path, location, '${ReCase(args['name']).snakeCase}.dart')); await _createFile(directory, file, args); diff --git a/lib/src/commands/make/service.dart b/lib/src/commands/make/service.dart index 680e6566b..00f13d56d 100644 --- a/lib/src/commands/make/service.dart +++ b/lib/src/commands/make/service.dart @@ -1,17 +1,18 @@ import 'dart:io'; +import 'package:mineral/src/internal/services/console/console_service.dart'; +import 'package:mineral_contract/mineral_contract.dart'; +import 'package:prompts/prompts.dart' as prompts; import 'package:mineral/src/commands/templates/event.dart'; -import 'package:mineral_cli/mineral_cli.dart'; -import 'package:mineral_console/mineral_console.dart'; import 'package:path/path.dart'; import 'package:recase/recase.dart'; -class MakeService extends CliCommand { - MakeService(Console console): super(console, 'make:service', 'Create new service file', ['name']); +class MakeService extends CliCommandContract { + MakeService(ConsoleService console): super(console, 'make:service', 'Create new service file', ['name']); @override Future handle(Map args) async { - final placeInRootFolder = Confirm('Would you like to change the location of your service file ?', defaultValue: false).build(); + final placeInRootFolder = prompts.getBool('Would you like to change the location of your ?', defaultsTo: false); if (!placeInRootFolder) { final file = File(join(Directory.current.path, 'src', '${ReCase(args['name']).snakeCase}.dart')); @@ -19,7 +20,7 @@ class MakeService extends CliCommand { return; } - final location = Ask('Where do you want to create your event file ?').build(); + final location = prompts.get('Where do you want to create your service ?'); final file = File(join(Directory.current.path, 'src', location, '${ReCase(args['name']).snakeCase}.dart')); await _createFile(file, args); } diff --git a/lib/src/commands/make/shared_state.dart b/lib/src/commands/make/shared_state.dart index 6ddcf4b55..eafe09433 100644 --- a/lib/src/commands/make/shared_state.dart +++ b/lib/src/commands/make/shared_state.dart @@ -1,17 +1,18 @@ import 'dart:io'; +import 'package:mineral_contract/mineral_contract.dart'; +import 'package:prompts/prompts.dart' as prompts; import 'package:mineral/src/commands/templates/shared_state.dart'; -import 'package:mineral_cli/mineral_cli.dart'; -import 'package:mineral_console/mineral_console.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; import 'package:path/path.dart'; import 'package:recase/recase.dart'; -class MakeSharedState extends CliCommand { - MakeSharedState(Console console): super(console, 'make:state', 'Create new shared state file', ['name']); +class MakeSharedState extends CliCommandContract { + MakeSharedState(ConsoleService console): super(console, 'make:state', 'Create new shared state file', ['name']); @override Future handle(Map args) async { - final placeInRootFolder = Confirm('Would you like to change the location of your ?', defaultValue: false).build(); + final placeInRootFolder = prompts.getBool('Would you like to change the location of your ?', defaultsTo: false); if (!placeInRootFolder) { final file = File(join(Directory.current.path, 'src', '${ReCase(args['name']).snakeCase}.dart')); @@ -19,7 +20,7 @@ class MakeSharedState extends CliCommand { return; } - final location = Ask('Where do you want to create your command file ?').build(); + final location = prompts.get('Where do you want to create your state file ?'); final file = File(join(Directory.current.path, 'src', location, '${ReCase(args['name']).snakeCase}.dart')); await _createFile(file, args); diff --git a/lib/src/internal/services/cli/command_line_interface.dart b/lib/src/internal/services/cli/command_line_interface.dart index f9ea6bcac..7597dc152 100644 --- a/lib/src/internal/services/cli/command_line_interface.dart +++ b/lib/src/internal/services/cli/command_line_interface.dart @@ -26,7 +26,7 @@ class CommandLineInterface extends CliServiceContract { MakeEvent(_console), MakeCommand(_console), MakeSharedState(_console), - MakeModule(_console), + MakePackage(_console), MakeService(_console), CompileExecutable(_console), CompileJavascript(_console), diff --git a/lib/src/internal/services/console/component.dart b/lib/src/internal/services/console/component.dart new file mode 100644 index 000000000..455546a20 --- /dev/null +++ b/lib/src/internal/services/console/component.dart @@ -0,0 +1,39 @@ +import 'dart:io'; + +import 'package:mineral/src/internal/services/console/ansi.dart'; +import 'package:mineral/src/internal/services/console/themes/default_theme.dart'; + +abstract class Component { + final DefaultTheme theme = DefaultTheme(); + + T build (); + + void clearLastLine () { + stdout.write(ansiCursorLeft + ansiCursorUp); + stdout.write(' ' * (stdout.terminalColumns + 1)); + stdout.write(ansiCursorLeft + ansiCursorUp + ansiCursorUp); + } + + void clearScreen () { + stdout.write('\x1B[2J\x1B[0;0H'); + } + + void updateLastLine (String sentence) { + clearLastLine(); + stdout.write(sentence); + _completeWithEmptyLine(sentence.length); + newLine(); + } + + void _completeWithEmptyLine (int length) { + stdout.write(' ' * (stdout.terminalColumns - length)); + } + + void newLine () { + stdout.writeln(''); + } + + void success (String message) { + stdout.writeln('${theme.successPrefix} $message'); + } +} \ No newline at end of file From c66e151dfd2e67a5492a28cc28fced6549fc3ab9 Mon Sep 17 00:00:00 2001 From: LeadcodeDev Date: Tue, 25 Apr 2023 00:53:36 +0200 Subject: [PATCH 4/6] feat: Add default console theme --- .../services/console/themes/default_theme.dart | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lib/src/internal/services/console/themes/default_theme.dart diff --git a/lib/src/internal/services/console/themes/default_theme.dart b/lib/src/internal/services/console/themes/default_theme.dart new file mode 100644 index 000000000..f409bf330 --- /dev/null +++ b/lib/src/internal/services/console/themes/default_theme.dart @@ -0,0 +1,14 @@ +import 'package:mineral/src/internal/services/console/theme.dart'; +import 'package:tint/tint.dart'; + +class DefaultTheme extends Theme { + DefaultTheme(): super( + inputPrefix: '?'.padRight(2).yellow(), + successPrefix: '✓'.padRight(2).green(), + errorPrefix: '✘'.padRight(2).red(), + booleanPrefix: '(y/n)'.padLeft(2).grey(), + pickedItemPrefix: '❯'.green(), + infoPrefix: '!'.cyan(), + warnPrefix: ''.yellow(), + ); +} \ No newline at end of file From 863ca0ca1538c6659467d2839d55c11be607f038 Mon Sep 17 00:00:00 2001 From: LeadcodeDev Date: Tue, 25 Apr 2023 00:53:48 +0200 Subject: [PATCH 5/6] feat: Improve command --- lib/src/commands/make/command.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/src/commands/make/command.dart b/lib/src/commands/make/command.dart index 68b05faa7..df686e661 100644 --- a/lib/src/commands/make/command.dart +++ b/lib/src/commands/make/command.dart @@ -1,17 +1,18 @@ import 'dart:io'; +import 'package:mineral_contract/mineral_contract.dart'; +import 'package:prompts/prompts.dart' as prompts; import 'package:mineral/src/commands/templates/command.dart'; -import 'package:mineral_cli/mineral_cli.dart'; -import 'package:mineral_console/mineral_console.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; import 'package:path/path.dart'; import 'package:recase/recase.dart'; -class MakeCommand extends CliCommand { - MakeCommand(Console console): super(console, 'make:command', 'Create new command file', ['name']); +class MakeCommand extends CliCommandContract { + MakeCommand(ConsoleService console): super(console, 'make:command', 'Create new command file', ['name']); @override Future handle(Map args) async { - final placeInRootFolder = Confirm('Would you like to change the location of your ?', defaultValue: false).build(); + final placeInRootFolder = prompts.getBool('Would you like to change the location of your ?', defaultsTo: false); if (!placeInRootFolder) { final file = File(join(Directory.current.path, 'src', '${ReCase(args['name']).snakeCase}.dart')); @@ -19,7 +20,7 @@ class MakeCommand extends CliCommand { return; } - final location = Ask('Where do you want to create your command file ?').build(); + final location = prompts.get('Where do you want to create your command file ?'); final file = File(join(Directory.current.path, 'src', location, '${ReCase(args['name']).snakeCase}.dart')); await _createFile(file, args); From cf4b080b43c7be146a2816c74672f2e7692c5037 Mon Sep 17 00:00:00 2001 From: LeadcodeDev Date: Tue, 25 Apr 2023 00:54:34 +0200 Subject: [PATCH 6/6] refactor: Some changes --- lib/src/api/channels/guild_channel.dart | 6 +-- lib/src/api/channels/news_channel.dart | 5 +-- lib/src/api/channels/partial_channel.dart | 8 ++-- lib/src/api/channels/text_based_channel.dart | 4 -- lib/src/api/guilds/guild.dart | 6 +-- lib/src/api/managers/message_manager.dart | 6 +-- lib/src/api/managers/sticker_manager.dart | 4 +- lib/src/api/managers/voice_manager.dart | 8 ++-- lib/src/api/messages/message.dart | 8 ++-- lib/src/api/users/user.dart | 4 +- lib/src/exceptions/api_exception.dart | 1 - lib/src/internal/kernel.dart | 39 ++----------------- .../console/contracts/theme_contract.dart | 9 +++++ .../internal/services/debugger_service.dart | 2 +- .../internal/websockets/sharding/shard.dart | 12 +++--- pubspec.yaml | 6 +-- 16 files changed, 49 insertions(+), 79 deletions(-) create mode 100644 lib/src/internal/services/console/contracts/theme_contract.dart diff --git a/lib/src/api/channels/guild_channel.dart b/lib/src/api/channels/guild_channel.dart index d55b0f395..45c91e499 100644 --- a/lib/src/api/channels/guild_channel.dart +++ b/lib/src/api/channels/guild_channel.dart @@ -4,7 +4,7 @@ import 'package:mineral/core/api.dart'; import 'package:mineral/framework.dart'; import 'package:mineral/src/api/builders/channel_builder.dart'; import 'package:mineral/src/api/managers/permission_overwrite_manager.dart'; -import 'package:mineral_cli/mineral_cli.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; import 'package:mineral_ioc/ioc.dart'; class GuildChannel extends PartialChannel { @@ -90,12 +90,12 @@ class GuildChannel extends PartialChannel { bool _validate () { if (type == ChannelType.guildCategory) { - ioc.use().console.warn('A category channel cannot have a parent'); + ioc.use().warn('A category channel cannot have a parent'); return false; } if (type == ChannelType.guildPublicThread || type == ChannelType.private || type == ChannelType.guildNewsThread) { - ioc.use().console.warn('A thread channel cannot change a parent'); + ioc.use().warn('A thread channel cannot change a parent'); return false; } diff --git a/lib/src/api/channels/news_channel.dart b/lib/src/api/channels/news_channel.dart index 4f90a8a7c..cf8b64c74 100644 --- a/lib/src/api/channels/news_channel.dart +++ b/lib/src/api/channels/news_channel.dart @@ -1,10 +1,9 @@ import 'package:mineral/core.dart'; import 'package:mineral/core/api.dart'; -import 'package:mineral/src/api/managers/message_manager.dart'; import 'package:mineral/src/api/managers/permission_overwrite_manager.dart'; import 'package:mineral/src/api/managers/thread_manager.dart'; import 'package:mineral/src/api/managers/webhook_manager.dart'; -import 'package:mineral_cli/mineral_cli.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; import 'package:mineral_ioc/ioc.dart'; class NewsChannel extends TextChannel { @@ -30,7 +29,7 @@ class NewsChannel extends TextChannel { /// Follow this by an webhook application Future follow (Snowflake webhookId) async { if (type != ChannelType.guildNews) { - ioc.use().console.warn('Impossible to follow the channel $id as it is not an announcement channel'); + ioc.use().warn('Impossible to follow the channel $id as it is not an announcement channel'); return; } diff --git a/lib/src/api/channels/partial_channel.dart b/lib/src/api/channels/partial_channel.dart index 854fcfbb9..b11fad062 100644 --- a/lib/src/api/channels/partial_channel.dart +++ b/lib/src/api/channels/partial_channel.dart @@ -1,9 +1,9 @@ import 'package:collection/collection.dart'; import 'package:mineral/core/api.dart'; -import 'package:mineral/src/api/channels/dm_channel.dart'; import 'package:mineral/src/api/channels/news_channel.dart'; import 'package:mineral/src/api/channels/stage_channel.dart'; -import 'package:mineral_console/mineral_console.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; +import 'package:mineral_ioc/ioc.dart'; class PartialChannel { final Snowflake _id; @@ -46,7 +46,7 @@ class ChannelWrapper { final ChannelType? channelType = ChannelType.values.firstWhereOrNull((element) => element.value == payload['type']); if (channelType == null) { - Console(theme: DefaultTheme()).warn("Guild channel ${payload['type']} don't exist! Please report this to our team."); + ioc.use().warn("Guild channel ${payload['type']} don't exist! Please report this to our team."); return null; } @@ -72,7 +72,7 @@ class ChannelWrapper { case ChannelType.guildForum: return ForumChannel.fromPayload(payload); default: - Console(theme: DefaultTheme()).warn('$channelType is not supported'); + ioc.use().warn('$channelType is not supported'); } return null; diff --git a/lib/src/api/channels/text_based_channel.dart b/lib/src/api/channels/text_based_channel.dart index 968d3ce56..646cf8f12 100644 --- a/lib/src/api/channels/text_based_channel.dart +++ b/lib/src/api/channels/text_based_channel.dart @@ -1,10 +1,6 @@ import 'package:mineral/core/api.dart'; -import 'package:mineral/framework.dart'; import 'package:mineral/src/api/builders/channel_builder.dart'; import 'package:mineral/src/api/managers/webhook_manager.dart'; -import 'package:mineral_ioc/ioc.dart'; -import 'package:mineral/core.dart'; -import 'package:mineral_cli/mineral_cli.dart'; class TextBasedChannel extends PartialTextChannel { final bool _nsfw; diff --git a/lib/src/api/guilds/guild.dart b/lib/src/api/guilds/guild.dart index 060da2a59..1a62fd4c7 100644 --- a/lib/src/api/guilds/guild.dart +++ b/lib/src/api/guilds/guild.dart @@ -19,7 +19,7 @@ import 'package:mineral/src/api/managers/sticker_manager.dart'; import 'package:mineral/src/api/managers/webhook_manager.dart'; import 'package:mineral/src/api/welcome_screen.dart'; import 'package:mineral/src/helper.dart'; -import 'package:mineral_cli/mineral_cli.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; import 'package:mineral_ioc/ioc.dart'; enum VerificationLevel { @@ -304,7 +304,7 @@ class Guild { MineralClient client = ioc.use(); if (owner.id != client.user.id) { - ioc.use().console.error("You cannot change the owner of the server because it does not belong to the ${client.user.username} client."); + ioc.use().error("You cannot change the owner of the server because it does not belong to the ${client.user.username} client."); return; } @@ -626,7 +626,7 @@ class Guild { for (String element in payload['features']) { GuildFeature? feature = GuildFeature.values.firstWhereOrNull((feature) => feature.value == element); if (feature == null) { - ioc.use().console.warn('Guild feature $element don\'t exist! Please report this to our team.'); + ioc.use().warn('Guild feature $element don\'t exist! Please report this to our team.'); } else { features.add(feature); } diff --git a/lib/src/api/managers/message_manager.dart b/lib/src/api/managers/message_manager.dart index 71ad4a0af..b705aa992 100644 --- a/lib/src/api/managers/message_manager.dart +++ b/lib/src/api/managers/message_manager.dart @@ -6,9 +6,8 @@ import 'package:mineral/core/api.dart'; import 'package:mineral/exception.dart'; import 'package:mineral/framework.dart'; import 'package:mineral/src/api/managers/cache_manager.dart'; -import 'package:mineral/src/api/messages/dm_message.dart'; import 'package:mineral/src/api/messages/partial_message.dart'; -import 'package:mineral_cli/mineral_cli.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; import 'package:mineral_ioc/ioc.dart'; class MessageManager extends CacheManager { @@ -111,8 +110,7 @@ class BulkDeleteBuilder { /// Future amount(int amount, {String? reason}) async { if (amount >= maxMessages || amount <= minMessages) { - return ioc.use() - .console.error('Provided too few or too many messages to delete. Must provide at least $minMessages and at most $maxMessages messages to delete. Action canceled'); + return ioc.use().error('Provided too few or too many messages to delete. Must provide at least $minMessages and at most $maxMessages messages to delete. Action canceled'); } if(_manager.cache.isEmpty || _manager.cache.length < amount) { diff --git a/lib/src/api/managers/sticker_manager.dart b/lib/src/api/managers/sticker_manager.dart index 75086c12d..cc2473afe 100644 --- a/lib/src/api/managers/sticker_manager.dart +++ b/lib/src/api/managers/sticker_manager.dart @@ -8,7 +8,7 @@ import 'package:mineral/framework.dart'; import 'package:mineral/src/api/managers/cache_manager.dart'; import 'package:mineral/src/api/sticker.dart'; import 'package:mineral/src/helper.dart'; -import 'package:mineral_cli/mineral_cli.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; import 'package:mineral_ioc/ioc.dart'; class StickerManager extends CacheManager { @@ -34,7 +34,7 @@ class StickerManager extends CacheManager { return sticker; } - ioc.use().console.warn('cancelled ${guild.name} guild does not have the ${GuildFeature.verified} or ${GuildFeature.partnered} feature.'); + ioc.use().warn('cancelled ${guild.name} guild does not have the ${GuildFeature.verified} or ${GuildFeature.partnered} feature.'); return null; } diff --git a/lib/src/api/managers/voice_manager.dart b/lib/src/api/managers/voice_manager.dart index 5daeb799f..627e8e505 100644 --- a/lib/src/api/managers/voice_manager.dart +++ b/lib/src/api/managers/voice_manager.dart @@ -2,7 +2,7 @@ import 'package:http/http.dart'; import 'package:mineral/core.dart'; import 'package:mineral/core/api.dart'; import 'package:mineral/framework.dart'; -import 'package:mineral_cli/mineral_cli.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; import 'package:mineral_ioc/ioc.dart'; class VoiceManager { @@ -49,7 +49,7 @@ class VoiceManager { return; } - ioc.use().console.error('Unable to ${value ? 'mute' : 'unmute'} user #$_memberId'); + ioc.use().error('Unable to ${value ? 'mute' : 'unmute'} user #$_memberId'); } /// ### Deafens or not a server member @@ -71,7 +71,7 @@ class VoiceManager { return; } - ioc.use().console.error('Unable to ${value ? 'deaf' : 'undeaf'} user #$_memberId'); + ioc.use().error('Unable to ${value ? 'deaf' : 'undeaf'} user #$_memberId'); } /// ### Moves a member from one voice channel to another @@ -111,7 +111,7 @@ class VoiceManager { return; } - ioc.use().console.error('Unable to move user $_memberId to $channelId'); + ioc.use().error('Unable to move user $_memberId to $channelId'); } factory VoiceManager.from(dynamic payload, Snowflake guildId) { diff --git a/lib/src/api/messages/message.dart b/lib/src/api/messages/message.dart index d5d2a5b20..1b485f605 100644 --- a/lib/src/api/messages/message.dart +++ b/lib/src/api/messages/message.dart @@ -11,7 +11,7 @@ import 'package:mineral/src/api/messages/message_author.dart'; import 'package:mineral/src/api/messages/message_mention.dart'; import 'package:mineral/src/api/messages/message_sticker_item.dart'; import 'package:mineral/src/api/messages/partial_message.dart'; -import 'package:mineral_cli/mineral_cli.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; import 'package:mineral_ioc/ioc.dart'; import 'package:mineral/src/internal/mixins/mineral_client.dart'; @@ -69,7 +69,7 @@ class Message extends PartialMessage { Future crossPost () async { if (channel.type != ChannelType.guildNews) { - ioc.use().console.warn('Message $id cannot be cross-posted as it is not in an announcement channel'); + ioc.use().warn('Message $id cannot be cross-posted as it is not in an announcement channel'); return; } @@ -79,7 +79,7 @@ class Message extends PartialMessage { Future pin (Snowflake webhookId) async { if (isPinned) { - ioc.use().console.warn('Message $id is already pinned'); + ioc.use().warn('Message $id is already pinned'); return; } @@ -89,7 +89,7 @@ class Message extends PartialMessage { Future unpin () async { if (!isPinned) { - ioc.use().console.warn('Message $id isn\'t pinned'); + ioc.use().warn('Message $id isn\'t pinned'); return; } diff --git a/lib/src/api/users/user.dart b/lib/src/api/users/user.dart index 0e09549ae..2bad37950 100644 --- a/lib/src/api/users/user.dart +++ b/lib/src/api/users/user.dart @@ -7,7 +7,7 @@ import 'package:mineral/core/builders.dart'; import 'package:mineral/framework.dart'; import 'package:mineral/src/api/users/user_flag.dart'; import 'package:mineral/src/internal/mixins/mineral_client.dart'; -import 'package:mineral_cli/mineral_cli.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; import 'package:mineral_ioc/ioc.dart'; class User { @@ -91,7 +91,7 @@ class User { return message; } - ioc.use().console.warn(payload['message']); + ioc.use().warn(payload['message']); return null; } diff --git a/lib/src/exceptions/api_exception.dart b/lib/src/exceptions/api_exception.dart index 2a76b9b36..3912ad86b 100644 --- a/lib/src/exceptions/api_exception.dart +++ b/lib/src/exceptions/api_exception.dart @@ -1,5 +1,4 @@ import 'package:mineral/core/extras.dart'; -import 'package:mineral/framework.dart'; import 'package:mineral/src/internal/services/debugger_service.dart'; class ApiException with Container, Console implements Exception { diff --git a/lib/src/internal/kernel.dart b/lib/src/internal/kernel.dart index a87f34897..535290350 100644 --- a/lib/src/internal/kernel.dart +++ b/lib/src/internal/kernel.dart @@ -1,18 +1,10 @@ -import 'dart:io'; - import 'package:mineral/core.dart'; import 'package:mineral/exception.dart'; -import 'package:mineral/src/commands/compile/exe.dart'; -import 'package:mineral/src/commands/compile/js.dart'; -import 'package:mineral/src/commands/help.dart'; -import 'package:mineral/src/commands/make/command.dart'; -import 'package:mineral/src/commands/make/event.dart'; -import 'package:mineral/src/commands/make/module.dart'; -import 'package:mineral/src/commands/make/service.dart'; -import 'package:mineral/src/commands/make/shared_state.dart'; import 'package:mineral/src/internal/mixins/container.dart'; import 'package:mineral/src/internal/services/collector_service.dart'; import 'package:mineral/src/internal/services/command_service.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; +import 'package:mineral/src/internal/services/console/themes/console_theme.dart'; import 'package:mineral/src/internal/services/context_menu_service.dart'; import 'package:mineral/src/internal/services/debugger_service.dart'; import 'package:mineral/src/internal/services/discord_api_http_service.dart'; @@ -21,17 +13,13 @@ import 'package:mineral/src/internal/services/event_service.dart'; import 'package:mineral/src/internal/services/intent_service.dart'; import 'package:mineral/src/internal/services/package_service.dart'; import 'package:mineral/src/internal/services/shared_state_service.dart'; -import 'package:mineral/src/internal/themes/mineral_theme.dart'; import 'package:mineral/src/internal/websockets/sharding/shard_manager.dart'; -import 'package:mineral_cli/mineral_cli.dart'; -import 'package:mineral_console/mineral_console.dart'; class Kernel with Container { final IntentService intents; final EnvironmentService _environment = EnvironmentService(); - final MineralCliContract cli = MineralCli(MineralTheme()); - + final ConsoleService _console = ConsoleService(theme: ConsoleTheme()); late EventService events; late CommandService commands; late SharedStateService states; @@ -56,23 +44,8 @@ class Kernel with Container { DebuggerService('[ debug ]'); } - void loadConsole () { - stdin.lineMode = true; - final console = Console(theme: DefaultTheme()); - - cli.register([ - MakeEvent(console), - MakeCommand(console), - MakeSharedState(console), - MakeModule(console), - MakeService(console), - CompileExecutable(console), - CompileJavascript(console), - Help(console, cli), - ]); - } - Future init () async { + container.bind((_) => _console); await _environment.load(); final DiscordApiHttpService http = DiscordApiHttpService('https://discord.com/api'); @@ -91,8 +64,4 @@ class Kernel with Container { ShardManager shardManager = ShardManager(http, token, intents.list); shardManager.start(shardsCount: (shardsCount != null ? int.tryParse(shardsCount) : null)); } - - void defineConsoleTheme (Theme theme) { - cli.defineConsoleTheme(theme); - } } diff --git a/lib/src/internal/services/console/contracts/theme_contract.dart b/lib/src/internal/services/console/contracts/theme_contract.dart new file mode 100644 index 000000000..ba40644fd --- /dev/null +++ b/lib/src/internal/services/console/contracts/theme_contract.dart @@ -0,0 +1,9 @@ +abstract class ThemeContract { + abstract final String inputPrefix; + abstract final String successPrefix; + abstract final String errorPrefix; + abstract final String booleanPrefix; + abstract final String pickedItemPrefix; + abstract final String infoPrefix; + abstract final String warnPrefix; +} \ No newline at end of file diff --git a/lib/src/internal/services/debugger_service.dart b/lib/src/internal/services/debugger_service.dart index 80de6da7f..0ff7396ca 100644 --- a/lib/src/internal/services/debugger_service.dart +++ b/lib/src/internal/services/debugger_service.dart @@ -2,9 +2,9 @@ import 'dart:io'; import 'package:mineral/core/extras.dart'; import 'package:mineral/src/internal/services/environment_service.dart'; -import 'package:mineral_console/mineral_console.dart'; import 'package:mineral_ioc/ioc.dart'; import 'package:path/path.dart'; +import 'package:tint/tint.dart'; class DebuggerService extends MineralService with Container { final String prefix; diff --git a/lib/src/internal/websockets/sharding/shard.dart b/lib/src/internal/websockets/sharding/shard.dart index 5b2f71c68..a51a1d782 100644 --- a/lib/src/internal/websockets/sharding/shard.dart +++ b/lib/src/internal/websockets/sharding/shard.dart @@ -9,6 +9,7 @@ import 'package:mineral/core/api.dart'; import 'package:mineral/core/extras.dart'; import 'package:mineral/exception.dart'; import 'package:mineral/src/exceptions/shard_exception.dart'; +import 'package:mineral/src/internal/services/console/console_service.dart'; import 'package:mineral/src/internal/services/debugger_service.dart'; import 'package:mineral/src/internal/websockets/heartbeat.dart'; import 'package:mineral/src/internal/websockets/sharding/shard_handler.dart'; @@ -16,7 +17,6 @@ import 'package:mineral/src/internal/websockets/sharding/shard_manager.dart'; import 'package:mineral/src/internal/websockets/sharding/shard_message.dart'; import 'package:mineral/src/internal/websockets/websocket_dispatcher.dart'; import 'package:mineral/src/internal/websockets/websocket_response.dart'; -import 'package:mineral_cli/mineral_cli.dart'; /// Represents a Discord Shard. /// A Shard is the object used to interact with the discord websocket. @@ -128,7 +128,7 @@ class Shard with Container { case ShardCommand.error: final String error = 'Shard #$id ${message.data['reason']} | ${message.data['code']}'; debugger.debug(error); - container.use().console.error(error); + container.use().error(error); final Map errors = { 4000: () => reconnect(resume: true), @@ -142,7 +142,7 @@ class Shard with Container { 4005: () => reconnect(resume: true), 4007: () => reconnect(resume: false), 4008: () => { - container.use().console.warn('Shard #$id : You send to many packets!'), + container.use().warn('Shard #$id : You send to many packets!'), reconnect(resume: false) }, 4009: () => reconnect(resume: true), @@ -155,14 +155,14 @@ class Shard with Container { return errorCallback(); } - container.use().console.error('Shard #$id : No error callback'); + container.use().error('Shard #$id : No error callback'); break; case ShardCommand.disconnected: if (_pendingReconnect) { return debugger.debug('Shard #$id : Websocket disconnected for reconnection'); } - container.use().console.warn('Shard #$id : Websocket disconnected without error, try to reconnect...'); + container.use().warn('Shard #$id : Websocket disconnected without error, try to reconnect...'); return reconnect(resume: true); case ShardCommand.terminateOk: debugger.debug('Shard #$id : Websocket connection terminated, restart...'); @@ -172,7 +172,7 @@ class Shard with Container { break; default: final String error = 'Shard #$id : Websocket disconnected for reconnection'; - container.use().console.error('Shard #$id : Unhandled message : ${message.command.name}'); + container.use().error('Shard #$id : Unhandled message : ${message.command.name}'); return debugger.debug(error); } } diff --git a/pubspec.yaml b/pubspec.yaml index e342aaa41..39b005db8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,10 +19,10 @@ dependencies: http: ^0.13.4 collection: ^1.16.0 mineral_ioc: ^2.0.0 - mineral_cli: ^1.1.1 - mineral_console: ^1.3.0 recase: ^4.1.0 - mineral_contract: ^1.2.0 + mineral_contract: ^1.4.0 + prompts: ^2.0.0 + tint: ^2.0.1 dev_dependencies: lints: ^1.0.0