Perintah
Pengantar
Perintah memungkinkan Anda memperluas CLI Nylo Website dengan tooling khusus proyek. Dengan membuat subclass dari NyCustomCommand, Anda dapat mengotomatiskan tugas-tugas berulang, membangun alur kerja deployment, menghasilkan kode, atau menambahkan fungsionalitas apa pun yang Anda butuhkan langsung di terminal.
Setiap perintah kustom memiliki akses ke sekumpulan helper bawaan untuk file I/O, JSON/YAML, prompt interaktif, spinner, progress bar, permintaan API, dan lainnya -- semuanya tanpa perlu mengimpor paket tambahan.
Catatan: Perintah kustom berjalan di luar runtime Flutter. Anda tidak dapat mengimpor
nylo_framework.dartdi perintah Anda. Gunakanny_cli.dartsebagai gantinya.
Membuat Perintah
Buat perintah baru menggunakan Metro atau Dart CLI:
metro make:command current_time
Anda dapat menentukan kategori untuk perintah Anda menggunakan opsi --category:
metro make:command current_time --category="project"
Ini akan membuat file baru di lib/app/commands/current_time.dart dan mendaftarkannya di registri perintah.
Struktur Perintah
Setiap perintah meng-extend NyCustomCommand dan mengimplementasikan dua method utama:
builder()-- mengonfigurasi opsi dan flaghandle()-- mengeksekusi logika perintah
import 'package:nylo_framework/metro/ny_cli.dart';
void main(arguments) => _CurrentTimeCommand(arguments).run();
/// Current Time Command
///
/// Usage:
/// metro app:current_time
class _CurrentTimeCommand extends NyCustomCommand {
_CurrentTimeCommand(super.arguments);
@override
CommandBuilder builder(CommandBuilder command) {
command.addOption('format', defaultValue: 'HH:mm:ss');
return command;
}
@override
Future<void> handle(CommandResult result) async {
final format = result.getString("format");
info("Current time format: $format");
}
}
Menjalankan Perintah
Jalankan perintah Anda menggunakan Metro atau Dart:
metro app:current_time
Nama perintah mengikuti pola category:name. Saat Anda menjalankan metro tanpa argumen, perintah kustom akan muncul di bagian Custom Commands:
[Custom Commands]
app:current_time
project:install_firebase
project:deploy
Untuk menampilkan bantuan untuk sebuah perintah:
metro app:current_time --help
Registri Perintah
Semua perintah kustom didaftarkan di lib/app/commands/commands.json. File ini diperbarui secara otomatis saat Anda menggunakan make:command:
[
{
"name": "install_firebase",
"category": "project",
"script": "install_firebase.dart"
},
{
"name": "current_time",
"category": "app",
"script": "current_time.dart"
}
]
Setiap entri memiliki:
| Field | Deskripsi |
|---|---|
name |
Nama perintah (digunakan setelah prefiks kategori) |
category |
Kategori perintah (misalnya app, project) |
script |
File Dart di lib/app/commands/ |
Opsi dan Flag
Konfigurasikan opsi dan flag perintah Anda di method builder() menggunakan CommandBuilder.
Menambahkan Opsi
Opsi menerima nilai dari pengguna:
@override
CommandBuilder builder(CommandBuilder command) {
command.addOption(
'environment',
abbr: 'e',
help: 'Target deployment environment',
defaultValue: 'development',
allowed: ['development', 'staging', 'production'],
);
return command;
}
Penggunaan:
metro project:deploy --environment=production
# atau menggunakan singkatan
metro project:deploy -e production
| Parameter | Tipe | Deskripsi |
|---|---|---|
name |
String |
Nama opsi |
abbr |
String? |
Singkatan satu karakter |
help |
String? |
Teks bantuan yang ditampilkan dengan --help |
allowed |
List<String>? |
Membatasi ke nilai yang diizinkan |
defaultValue |
String? |
Nilai default jika tidak diberikan |
Menambahkan Flag
Flag adalah toggle boolean:
@override
CommandBuilder builder(CommandBuilder command) {
command.addFlag(
'verbose',
abbr: 'v',
help: 'Enable verbose output',
defaultValue: false,
);
return command;
}
Penggunaan:
metro project:deploy --verbose
metro project:deploy -v
| Parameter | Tipe | Deskripsi |
|---|---|---|
name |
String |
Nama flag |
abbr |
String? |
Singkatan satu karakter |
help |
String? |
Teks bantuan yang ditampilkan dengan --help |
defaultValue |
bool |
Nilai default (default: false) |
Hasil Perintah
Method handle() menerima objek CommandResult dengan aksesor bertipe untuk membaca opsi, flag, dan argumen yang telah di-parse.
@override
Future<void> handle(CommandResult result) async {
// Get a string option
final name = result.getString('name');
// Get a boolean flag
final verbose = result.getBool('verbose');
// Get an integer option
final count = result.getInt('count');
// Generic typed access
final value = result.get<String>('key');
// Built-in flag checks
if (result.hasForceFlag) { /* --force was passed */ }
if (result.hasHelpFlag) { /* --help was passed */ }
// Raw arguments
List<String> allArgs = result.arguments;
List<String> unparsed = result.rest;
}
| Method / Properti | Mengembalikan | Deskripsi |
|---|---|---|
getString(String name, {String? defaultValue}) |
String? |
Mendapatkan nilai string |
getBool(String name, {bool? defaultValue}) |
bool? |
Mendapatkan nilai boolean |
getInt(String name, {int? defaultValue}) |
int? |
Mendapatkan nilai integer |
get<T>(String name) |
T? |
Mendapatkan nilai bertipe |
hasForceFlag |
bool |
Apakah --force diberikan |
hasHelpFlag |
bool |
Apakah --help diberikan |
arguments |
List<String> |
Semua argumen baris perintah |
rest |
List<String> |
Argumen rest yang belum di-parse |
Input Interaktif
NyCustomCommand menyediakan method untuk mengumpulkan input pengguna di terminal.
Input Teks
// Ask a question with optional default
final name = prompt('What is your project name?', defaultValue: 'my_app');
// ask() is an alias for prompt()
final description = ask('Enter a description:');
| Parameter | Tipe | Deskripsi |
|---|---|---|
question |
String |
Pertanyaan yang ditampilkan |
defaultValue |
String |
Nilai default jika pengguna menekan Enter (default: '') |
Konfirmasi
if (confirm('Would you like to continue?', defaultValue: true)) {
await runProcess('flutter pub get');
} else {
info('Operation canceled');
}
| Parameter | Tipe | Deskripsi |
|---|---|---|
question |
String |
Pertanyaan ya/tidak |
defaultValue |
bool |
Jawaban default (default: false) |
Pilihan Tunggal
final environment = select(
'Select deployment environment:',
['development', 'staging', 'production'],
defaultOption: 'development',
);
| Parameter | Tipe | Deskripsi |
|---|---|---|
question |
String |
Teks prompt |
options |
List<String> |
Pilihan yang tersedia |
defaultOption |
String? |
Opsi yang sudah dipilih sebelumnya |
Pilihan Ganda
final packages = multiSelect(
'Select packages to install:',
['firebase_auth', 'dio', 'provider', 'shared_preferences'],
);
if (packages.isNotEmpty) {
addPackages(packages);
await runProcess('flutter pub get');
}
Pengguna memasukkan nomor yang dipisahkan koma atau "all".
Input Rahasia
final apiKey = promptSecret('Enter your API key:');
Input disembunyikan dari tampilan terminal. Akan kembali ke input yang terlihat jika mode echo tidak didukung.
Format Output
Method untuk mencetak output bergaya ke konsol:
@override
Future<void> handle(CommandResult result) async {
info('Processing files...'); // Blue text
error('Operation failed'); // Red text
success('Deployment complete'); // Green text
warning('Outdated package'); // Yellow text
line('Plain text output'); // No color
comment('Background note'); // Gray text
alert('Important notice'); // Bordered alert box
newLine(); // One blank line
newLine(3); // Three blank lines
// Exit the command with an error
abort('Fatal error occurred'); // Prints red, exits with code 1
}
| Method | Deskripsi |
|---|---|
info(String message) |
Mencetak teks biru |
error(String message) |
Mencetak teks merah |
success(String message) |
Mencetak teks hijau |
warning(String message) |
Mencetak teks kuning |
line(String message) |
Mencetak teks biasa (tanpa warna) |
newLine([int count = 1]) |
Mencetak baris kosong |
comment(String message) |
Mencetak teks abu-abu/redup |
alert(String message) |
Mencetak kotak peringatan berbingkai |
abort([String? message, int exitCode = 1]) |
Keluar dari perintah dengan error |
Spinner dan Progres
Spinner dan progress bar memberikan umpan balik visual selama operasi yang berjalan lama.
Menggunakan withSpinner
Bungkus tugas async dengan spinner otomatis:
final projectFiles = await withSpinner(
task: () async {
await sleep(2);
return ['pubspec.yaml', 'lib/main.dart', 'README.md'];
},
message: 'Analyzing project structure',
successMessage: 'Project analysis complete',
errorMessage: 'Failed to analyze project',
);
info('Found ${projectFiles.length} key files');
| Parameter | Tipe | Deskripsi |
|---|---|---|
task |
Future<T> Function() |
Fungsi async yang dieksekusi |
message |
String |
Teks yang ditampilkan saat spinner berjalan |
successMessage |
String? |
Ditampilkan saat berhasil |
errorMessage |
String? |
Ditampilkan saat gagal |
Kontrol Spinner Manual
Untuk alur kerja multi-langkah, buat spinner dan kontrol secara manual:
final spinner = createSpinner('Deploying to production');
spinner.start();
try {
await runProcess('flutter clean', silent: true);
spinner.update('Building release version');
await runProcess('flutter build web --release', silent: true);
spinner.update('Uploading to server');
await runProcess('./deploy.sh', silent: true);
spinner.stop(completionMessage: 'Deployment completed', success: true);
} catch (e) {
spinner.stop(completionMessage: 'Deployment failed: $e', success: false);
}
Method ConsoleSpinner:
| Method | Deskripsi |
|---|---|
start([String? message]) |
Memulai animasi spinner |
update(String message) |
Mengubah pesan yang ditampilkan |
stop({String? completionMessage, bool success = true}) |
Menghentikan spinner |
Progress Bar
Membuat dan mengelola progress bar secara manual:
final progress = progressBar(100, message: 'Processing files');
progress.start();
for (int i = 0; i < 100; i++) {
await Future.delayed(Duration(milliseconds: 50));
progress.tick();
}
progress.complete('All files processed');
Method ConsoleProgressBar:
| Method / Properti | Deskripsi |
|---|---|
start() |
Memulai progress bar |
tick([int amount = 1]) |
Menambah progres |
update(int value) |
Mengatur progres ke nilai tertentu |
updateMessage(String newMessage) |
Mengubah pesan yang ditampilkan |
complete([String? completionMessage]) |
Menyelesaikan dengan pesan opsional |
stop() |
Menghentikan tanpa menyelesaikan |
current |
Nilai progres saat ini (getter) |
percentage |
Progres sebagai persentase 0-100 (getter) |
Memproses Item dengan Progres
Memproses daftar item dengan pelacakan progres otomatis:
// Async processing
final results = await withProgress<File, String>(
items: findFiles('lib/', extension: '.dart'),
process: (file, index) async {
return file.path;
},
message: 'Analyzing Dart files',
completionMessage: 'Analysis complete',
);
// Synchronous processing
final upperItems = withProgressSync<String, String>(
items: ['a', 'b', 'c', 'd', 'e'],
process: (item, index) => item.toUpperCase(),
message: 'Converting items',
);
Helper API
Helper api menyediakan wrapper sederhana di atas Dio untuk membuat permintaan HTTP:
// GET request
final userData = await api((request) =>
request.get('https://api.example.com/users/1')
);
// POST request
final result = await api((request) =>
request.post(
'https://api.example.com/items',
data: {'name': 'New Item', 'price': 19.99},
)
);
// PUT request
final updateResult = await api((request) =>
request.put(
'https://api.example.com/items/42',
data: {'name': 'Updated Item', 'price': 29.99},
)
);
// DELETE request
final deleteResult = await api((request) =>
request.delete('https://api.example.com/items/42')
);
// PATCH request
final patchResult = await api((request) =>
request.patch(
'https://api.example.com/items/42',
data: {'price': 24.99},
)
);
// With query parameters
final searchResults = await api((request) =>
request.get(
'https://api.example.com/search',
queryParameters: {'q': 'keyword', 'limit': 10},
)
);
Kombinasikan dengan withSpinner untuk pengalaman pengguna yang lebih baik:
final data = await withSpinner(
task: () => api((request) =>
request.get('https://api.example.com/config')
),
message: 'Loading configuration',
);
ApiService mendukung method get, post, put, delete, dan patch, masing-masing menerima parameter opsional queryParameters, data, options, dan cancelToken.
Helper Sistem File
Helper sistem file bawaan sehingga Anda tidak perlu mengimpor dart:io:
// Check existence
if (fileExists('lib/config/app.dart')) { /* ... */ }
if (directoryExists('lib/app/models')) { /* ... */ }
// Read files
String content = await readFile('pubspec.yaml');
String contentSync = readFileSync('pubspec.yaml');
// Write files
await writeFile('lib/generated/output.dart', 'class Output {}');
writeFileSync('lib/generated/output.dart', 'class Output {}');
// Append to a file
await appendFile('log.txt', 'New log entry\n');
// Ensure a directory exists
await ensureDirectory('lib/generated');
// Delete and copy files
await deleteFile('lib/generated/output.dart');
await copyFile('lib/config/app.dart', 'lib/config/app.bak.dart');
| Method | Deskripsi |
|---|---|
fileExists(String path) |
Mengembalikan true jika file ada |
directoryExists(String path) |
Mengembalikan true jika direktori ada |
readFile(String path) |
Membaca file sebagai string (async) |
readFileSync(String path) |
Membaca file sebagai string (sync) |
writeFile(String path, String content) |
Menulis konten ke file (async) |
writeFileSync(String path, String content) |
Menulis konten ke file (sync) |
appendFile(String path, String content) |
Menambahkan konten ke file |
ensureDirectory(String path) |
Membuat direktori jika belum ada |
deleteFile(String path) |
Menghapus file |
copyFile(String source, String destination) |
Menyalin file |
Helper JSON dan YAML
Membaca dan menulis file JSON dan YAML dengan helper bawaan:
// Read JSON as Map
Map<String, dynamic> config = await readJson('config.json');
// Read JSON as List
List<dynamic> items = await readJsonArray('lib/app/commands/commands.json');
// Write JSON (pretty printed by default)
await writeJson('output.json', {'name': 'MyApp', 'version': '1.0.0'});
// Write compact JSON
await writeJson('output.json', data, pretty: false);
// Append to a JSON array file (with duplicate prevention)
await appendToJsonArray(
'lib/app/commands/commands.json',
{'name': 'my_command', 'category': 'app', 'script': 'my_command.dart'},
uniqueKey: 'name',
);
// Read YAML as Map
Map<String, dynamic> pubspec = await readYaml('pubspec.yaml');
info('Project: ${pubspec['name']}');
| Method | Deskripsi |
|---|---|
readJson(String path) |
Membaca file JSON sebagai Map<String, dynamic> |
readJsonArray(String path) |
Membaca file JSON sebagai List<dynamic> |
writeJson(String path, dynamic data, {bool pretty = true}) |
Menulis data sebagai JSON |
appendToJsonArray(String path, Map item, {String? uniqueKey}) |
Menambahkan ke file array JSON |
readYaml(String path) |
Membaca file YAML sebagai Map<String, dynamic> |
Manipulasi File Dart
Helper untuk mengedit file sumber Dart secara programatik -- berguna saat membangun alat scaffolding:
// Add an import (skips if already present)
await addImport(
'lib/bootstrap/providers.dart',
"import '/app/providers/firebase_provider.dart';",
);
// Insert code before the last closing brace
await insertBeforeClosingBrace(
'lib/bootstrap/providers.dart',
' FirebaseProvider(),',
);
// Check if a file contains a string
bool hasImport = await fileContains(
'lib/bootstrap/providers.dart',
'firebase_provider',
);
// Check if a file matches a regex pattern
bool hasClass = await fileContainsPattern(
'lib/app/models/user.dart',
RegExp(r'class User'),
);
| Method | Deskripsi |
|---|---|
addImport(String filePath, String importStatement) |
Menambahkan import ke file Dart (dilewati jika sudah ada) |
insertBeforeClosingBrace(String filePath, String code) |
Menyisipkan kode sebelum } terakhir di file |
fileContains(String filePath, String identifier) |
Memeriksa apakah file berisi string tertentu |
fileContainsPattern(String filePath, Pattern pattern) |
Memeriksa apakah file cocok dengan pola tertentu |
Helper Direktori
Helper untuk bekerja dengan direktori dan menemukan file:
// List directory contents
var entities = listDirectory('lib/app/models');
var allEntities = listDirectory('lib/', recursive: true);
// Find files by extension
List<File> dartFiles = findFiles(
'lib/app/models',
extension: '.dart',
recursive: true,
);
// Find files by name pattern
List<File> testFiles = findFiles(
'test/',
namePattern: RegExp(r'_test\.dart$'),
);
// Delete a directory recursively
await deleteDirectory('build/');
// Copy a directory recursively
await copyDirectory('lib/templates', 'lib/generated');
| Method | Deskripsi |
|---|---|
listDirectory(String path, {bool recursive = false}) |
Menampilkan isi direktori |
findFiles(String directory, {String? extension, Pattern? namePattern, bool recursive = true}) |
Menemukan file yang sesuai kriteria |
deleteDirectory(String path) |
Menghapus direktori secara rekursif |
copyDirectory(String source, String destination) |
Menyalin direktori secara rekursif |
Helper Konversi Huruf
Mengonversi string antar konvensi penamaan tanpa mengimpor paket recase:
String input = 'user profile page';
snakeCase(input); // user_profile_page
camelCase(input); // userProfilePage
pascalCase(input); // UserProfilePage
titleCase(input); // User Profile Page
kebabCase(input); // user-profile-page
constantCase(input); // USER_PROFILE_PAGE
| Method | Format Output | Contoh |
|---|---|---|
snakeCase(String input) |
snake_case |
user_profile |
camelCase(String input) |
camelCase |
userProfile |
pascalCase(String input) |
PascalCase |
UserProfile |
titleCase(String input) |
Title Case |
User Profile |
kebabCase(String input) |
kebab-case |
user-profile |
constantCase(String input) |
CONSTANT_CASE |
USER_PROFILE |
Helper Path Proyek
Getter untuk direktori standar proyek Nylo Website, mengembalikan path relatif terhadap root proyek:
info(modelsPath); // lib/app/models
info(controllersPath); // lib/app/controllers
info(widgetsPath); // lib/resources/widgets
info(pagesPath); // lib/resources/pages
info(commandsPath); // lib/app/commands
info(configPath); // lib/config
info(providersPath); // lib/app/providers
info(eventsPath); // lib/app/events
info(networkingPath); // lib/app/networking
info(themesPath); // lib/resources/themes
// Build a custom path
String customPath = projectPath('app/services/auth_service.dart');
// Returns: lib/app/services/auth_service.dart
| Properti | Path |
|---|---|
modelsPath |
lib/app/models |
controllersPath |
lib/app/controllers |
widgetsPath |
lib/resources/widgets |
pagesPath |
lib/resources/pages |
commandsPath |
lib/app/commands |
configPath |
lib/config |
providersPath |
lib/app/providers |
eventsPath |
lib/app/events |
networkingPath |
lib/app/networking |
themesPath |
lib/resources/themes |
projectPath(String relativePath) |
Menyelesaikan path relatif di dalam proyek |
Helper Platform
Memeriksa platform dan mengakses variabel lingkungan:
if (isWindows) {
info('Running on Windows');
} else if (isMacOS) {
info('Running on macOS');
} else if (isLinux) {
info('Running on Linux');
}
info('Working in: $workingDirectory');
String home = env('HOME', '/default/path');
| Properti / Method | Deskripsi |
|---|---|
isWindows |
true jika berjalan di Windows |
isMacOS |
true jika berjalan di macOS |
isLinux |
true jika berjalan di Linux |
workingDirectory |
Path direktori kerja saat ini |
env(String key, [String defaultValue = '']) |
Membaca variabel lingkungan sistem |
Perintah Dart dan Flutter
Menjalankan perintah CLI Dart dan Flutter umum sebagai method helper. Masing-masing mengembalikan kode exit proses:
// Format a Dart file or directory
await dartFormat('lib/app/models/user.dart');
// Run dart analyze
int analyzeResult = await dartAnalyze('lib/');
// Run flutter pub get
await flutterPubGet();
// Run flutter clean
await flutterClean();
// Build for a target with additional args
await flutterBuild('apk', args: ['--release', '--split-per-abi']);
await flutterBuild('web', args: ['--release']);
// Run flutter test
await flutterTest();
await flutterTest('test/unit/');
| Method | Deskripsi |
|---|---|
dartFormat(String path) |
Menjalankan dart format pada file atau direktori |
dartAnalyze([String? path]) |
Menjalankan dart analyze |
flutterPubGet() |
Menjalankan flutter pub get |
flutterClean() |
Menjalankan flutter clean |
flutterBuild(String target, {List<String> args}) |
Menjalankan flutter build <target> |
flutterTest([String? path]) |
Menjalankan flutter test |
Helper Validasi
Helper untuk memvalidasi dan membersihkan input pengguna untuk pembuatan kode:
// Validate a Dart identifier
if (!isValidDartIdentifier('MyClass')) {
error('Invalid Dart identifier');
}
// Require a non-empty first argument (aborts if missing)
String name = requireArgument(result, message: 'Please provide a name');
// Clean a class name (PascalCase, remove suffixes)
String className = cleanClassName('user_model', removeSuffixes: ['_model']);
// Returns: 'User'
// Clean a file name (snake_case with extension)
String fileName = cleanFileName('UserModel', extension: '.dart');
// Returns: 'user_model.dart'
| Method | Deskripsi |
|---|---|
isValidDartIdentifier(String name) |
Memvalidasi nama identifier Dart |
requireArgument(CommandResult result, {String? message}) |
Memerlukan argumen pertama yang tidak kosong atau membatalkan |
cleanClassName(String name, {List<String> removeSuffixes}) |
Membersihkan dan mengubah nama kelas ke PascalCase |
cleanFileName(String name, {String extension = '.dart'}) |
Membersihkan dan mengubah nama file ke snake_case |
Scaffolding File
Membuat satu atau banyak file dengan konten menggunakan sistem scaffolding.
File Tunggal
await scaffold(
path: 'lib/app/services/auth_service.dart',
content: '''
class AuthService {
Future<bool> login(String email, String password) async {
return false;
}
}
''',
force: false,
successMessage: 'AuthService created',
);
| Parameter | Tipe | Deskripsi |
|---|---|---|
path |
String |
Path file yang akan dibuat |
content |
String |
Konten file |
force |
bool |
Timpa jika sudah ada (default: false) |
successMessage |
String? |
Pesan yang ditampilkan saat berhasil |
Banyak File
await scaffoldMany([
ScaffoldFile(
path: 'lib/app/models/product.dart',
content: 'class Product {}',
successMessage: 'Product model created',
),
ScaffoldFile(
path: 'lib/app/networking/product_api_service.dart',
content: 'class ProductApiService {}',
successMessage: 'Product API service created',
),
], force: false);
Kelas ScaffoldFile:
| Properti | Tipe | Deskripsi |
|---|---|---|
path |
String |
Path file yang akan dibuat |
content |
String |
Konten file |
successMessage |
String? |
Pesan yang ditampilkan saat berhasil |
Task Runner
Menjalankan serangkaian tugas bernama dengan output status otomatis.
Task Runner Dasar
await runTasks([
CommandTask(
'Clean project',
() => runProcess('flutter clean', silent: true),
),
CommandTask(
'Fetch dependencies',
() => runProcess('flutter pub get', silent: true),
),
CommandTask(
'Run tests',
() => runProcess('flutter test', silent: true),
stopOnError: true,
),
]);
Task Runner dengan Spinner
await runTasksWithSpinner([
CommandTask(
'Preparing release',
() async {
await flutterClean();
await flutterPubGet();
},
),
CommandTask(
'Building APK',
() => flutterBuild('apk', args: ['--release']),
),
]);
Kelas CommandTask:
| Properti | Tipe | Default | Deskripsi |
|---|---|---|---|
name |
String |
wajib | Nama tugas yang ditampilkan di output |
action |
Future<void> Function() |
wajib | Fungsi async yang dieksekusi |
stopOnError |
bool |
true |
Apakah menghentikan tugas yang tersisa jika gagal |
Output Tabel
Menampilkan tabel ASCII terformat di konsol:
table(
['Name', 'Version', 'Status'],
[
['nylo_framework', '7.0.0', 'installed'],
['nylo_support', '7.0.0', 'installed'],
['dio', '5.4.0', 'installed'],
],
);
Output:
┌─────────────────┬─────────┬───────────┐
│ Name │ Version │ Status │
├─────────────────┼─────────┼───────────┤
│ nylo_framework │ 7.0.0 │ installed │
│ nylo_support │ 7.0.0 │ installed │
│ dio │ 5.4.0 │ installed │
└─────────────────┴─────────┴───────────┘
Contoh
Perintah Waktu Saat Ini
Perintah sederhana yang menampilkan waktu saat ini:
import 'package:nylo_framework/metro/ny_cli.dart';
void main(arguments) => _CurrentTimeCommand(arguments).run();
class _CurrentTimeCommand extends NyCustomCommand {
_CurrentTimeCommand(super.arguments);
@override
CommandBuilder builder(CommandBuilder command) {
command.addOption('format', defaultValue: 'HH:mm:ss');
return command;
}
@override
Future<void> handle(CommandResult result) async {
final format = result.getString("format");
final now = DateTime.now();
info("The current time is ${now.toIso8601String()}");
info("Requested format: $format");
}
}
Perintah Unduh Font
Perintah yang mengunduh dan menginstal Google Fonts ke dalam proyek:
import 'package:nylo_framework/metro/ny_cli.dart';
void main(arguments) => _DownloadFontsCommand(arguments).run();
class _DownloadFontsCommand extends NyCustomCommand {
_DownloadFontsCommand(super.arguments);
@override
CommandBuilder builder(CommandBuilder command) {
command.addOption('font', abbr: 'f', help: 'Font family name');
command.addFlag('verbose', abbr: 'v', defaultValue: false);
return command;
}
@override
Future<void> handle(CommandResult result) async {
final fontName = result.getString('font') ??
prompt('Enter font family name:', defaultValue: 'Roboto');
final verbose = result.getBool('verbose') ?? false;
await withSpinner(
task: () async {
await ensureDirectory('assets/fonts');
final fontData = await api((request) =>
request.get('https://fonts.google.com/download?family=$fontName')
);
if (fontData != null) {
await writeFile('assets/fonts/$fontName.ttf', fontData.toString());
}
},
message: 'Downloading $fontName font',
successMessage: '$fontName font installed',
errorMessage: 'Failed to download $fontName',
);
if (verbose) {
info('Font saved to: assets/fonts/$fontName.ttf');
}
}
}
Perintah Pipeline Deployment
Perintah yang menjalankan pipeline deployment lengkap menggunakan task runner:
import 'package:nylo_framework/metro/ny_cli.dart';
void main(arguments) => _DeployCommand(arguments).run();
class _DeployCommand extends NyCustomCommand {
_DeployCommand(super.arguments);
@override
CommandBuilder builder(CommandBuilder command) {
command.addOption(
'environment',
abbr: 'e',
defaultValue: 'development',
allowed: ['development', 'staging', 'production'],
);
command.addFlag('skip-tests', defaultValue: false);
return command;
}
@override
Future<void> handle(CommandResult result) async {
final env = result.getString('environment') ?? 'development';
final skipTests = result.getBool('skip-tests') ?? false;
alert('Deploying to $env');
newLine();
if (env == 'production') {
if (!confirm('Are you sure you want to deploy to production?')) {
abort('Deployment canceled');
}
}
final tasks = <CommandTask>[
CommandTask('Clean project', () => flutterClean()),
CommandTask('Fetch dependencies', () => flutterPubGet()),
];
if (!skipTests) {
tasks.add(CommandTask(
'Run tests',
() => flutterTest(),
stopOnError: true,
));
}
tasks.add(CommandTask(
'Build for web',
() => flutterBuild('web', args: ['--release']),
));
await runTasksWithSpinner(tasks);
newLine();
success('Deployment to $env completed');
table(
['Step', 'Status'],
tasks.map((t) => [t.name, 'Done']).toList(),
);
}
}