ڈارٹ کے ساتھ کمانڈ لائن انٹرفیس (CLI) کی ترقی کو دریافت کریں: صفر سے مکمل طور پر کھلے ڈویلپر ٹولز تک۔

زیادہ تر ڈویلپر اپنے دن کا ایک اہم حصہ ٹرمینل میں گزارتے ہیں۔ وہ چلاتے ہیں flutter buildدھکا gitپیکیج مینجمنٹ dart pubکمانڈ لائن سے اپنی پائپ لائن کو ٹیون کریں۔ یہ تمام ٹولز CLI یا کمانڈ لائن انٹرفیس ہیں۔ یہ ایک ایسا پروگرام ہے جو ٹرمینل میں موجود ہے اور ٹیکسٹ کمانڈز کا جواب دیتا ہے۔

لیکن زیادہ تر ڈویلپرز نے کبھی نہیں بنایا۔

یہ ایک ضائع ہونے والا موقع ہے۔ CLI ٹولز سب سے زیادہ عملی ٹولز میں سے ایک ہیں جو ایک ڈویلپر پیش کر سکتا ہے۔ یہ دہرائے جانے والے ورک فلو کو خودکار بناتا ہے، ٹیموں میں عمل کو معیاری بناتا ہے، اور ایک بار شائع ہونے کے بعد، ایک ٹھوس نمونہ بن جاتا ہے جسے ڈویلپر کمیونٹی دریافت، انسٹال اور استعمال کر سکتی ہے۔

اس ہینڈ بک میں، ہم مکمل طور پر تقسیم شدہ ڈارٹ CLI ٹول بنانے کا عمل شروع کرتے ہیں۔ آئیے بنیادی باتوں کے ساتھ شروع کریں: CLI کیسے کام کرتا ہے، ڈارٹ ٹرمینل ان پٹ کو کیسے حاصل کرتا ہے اور اس پر کارروائی کرتا ہے، اور کلیدی نحو کے بارے میں جو آپ کو جاننے کی ضرورت ہے۔ اس کے بعد ہم تین بتدریج زیادہ پیچیدہ CLIs بناتے ہیں، جو بنیادی باتوں سے شروع ہوتے ہیں اور اصل API درخواست کے ایگزیکیوٹر کے ساتھ ختم ہوتے ہیں۔ آخر میں، ہم تقسیم کے تمام دستیاب راستوں کا احاطہ کریں گے۔ pub.dev مرتب شدہ بائنریز، ہومبریو ٹیبز، ڈوکر، اور مقامی ٹیم ایکٹیویشن کے لیے استعمال کیا جاتا ہے۔

گائیڈ کے اختتام تک، آپ دونوں کو سمجھ جائیں گے کہ ڈارٹ میں CLI ٹولز کیسے بنائے جائیں اور انہیں دوسرے ڈویلپرز کو عملی استعمال کے لیے کیسے دستیاب کیا جائے۔

انڈیکس

شرطیں

شروع کرنے سے پہلے، آپ کے پاس ہونا ضروری ہے:

  • ڈارٹ SDK انسٹال کریں (dart --version ٹرمینل میں کام کرنا چاہئے)

  • ڈارٹ نحو کا بنیادی علم

  • ٹرمینل اور چلانے کے احکامات کے ساتھ آرام

  • pub.dev اکاؤنٹ (پبلشنگ سیکشن کے لیے)

  • GitHub اکاؤنٹ (بائنری ڈسٹری بیوشن سیکشن کے لیے)

CLI کیا ہے اور میں اسے کیوں بناؤں؟

CLI (یا کمانڈ لائن انٹرفیس) ایک ایسا پروگرام ہے جو گرافیکل انٹرفیس میں بٹنوں اور اسکرینوں کے بجائے ٹرمینل میں ٹیکسٹ کمانڈز کے ذریعے مکمل طور پر انٹرایکٹو ہوتا ہے۔

ایک ڈویلپر کے طور پر، بہت سے ٹولز جو آپ پہلے ہی استعمال کرتے ہیں وہ CLI ٹولز ہیں۔

flutter build apk
git commit -m "fix: auth flow"
dart pub get
npm install

پھڑپھڑانا، گٹ، ڈارٹ، این پی ایم – تمام CLIs۔ آپ پہلے سے ہی روزانہ CLI صارف ہیں۔ یہ مضمون CLI بلڈر بننے کے طریقہ کے بارے میں ہے۔

ایک ڈویلپر کے طور پر، CLI ٹول بنانے کی تین زبردست وجوہات ہیں۔

  1. دہرائے جانے والے کاموں کو خودکار بنائیں: جو بھی چیز آپ ہفتے میں دو بار سے زیادہ داخل کرتے ہیں وہ آٹومیشن کے لیے اہل ہے۔ سی ایل آئی سات قدمی دستی عمل کو ایک واحد کمانڈ میں بدل دیتا ہے: بوائلر پلیٹ فولڈر کا ڈھانچہ بنانا، حکموں کی ترتیب کو چلانا، فائلوں کو سہاروں میں ڈھالنا، اور پہلے سے تعمیر شدہ ماحول کی تصدیق کرنا۔

  2. ٹیم ورک فلو کو معیاری بنائیں: README کے بجائے جو کہتا ہے کہ "ان کمانڈز کو اس ترتیب سے چلائیں”، ایک کمانڈ دیں جو ہر چیز کو مستقل طور پر، ہر بار، انسانی غلطی یا گمشدہ اقدامات کے بغیر کرے۔

  3. ٹولز بنائیں اور شائع کریں۔ شائع شدہ ڈارٹ سی ایل آئی پیکجز ٹھوس نمونے ہیں۔ یہ pub.dev پر نظر آتا ہے، دوسرے ڈویلپرز کے ذریعہ انسٹال اور استعمال کیا جاتا ہے، اور انجینئرنگ کی حقیقی گہرائی کو اس طرح بتاتا ہے جس طرح پورٹ فولیو یا ریزیوم نہیں کر سکتا۔

CLI تجزیہ

کوڈ کی ایک لائن لکھنے سے پہلے CLI کمانڈز کی ساخت کو سمجھنا مفید ہے۔ تمام احکامات ایک مستقل پیٹرن کی پیروی کرتے ہیں۔

tool [subcommand] [arguments] [options/flags]

اگر ہم ایک حقیقی مثال کا تجزیہ کریں تو وہ اس طرح ہے:

flutter build apk --release --obfuscate
│       │     │   │
tool    sub   arg  flags
  • سامان – پروگرام خود (flutter، dart، git)

  • ذیلی کمانڈ – تم کیا کر رہے ہو (build، run، pub)

  • عنصر – وہ اعتراض جس پر عمل کیا جاتا ہے (apk، main.dartفائل کا نام)

  • جھنڈے اور اختیارات – ترمیم کرنے والے جو رویے کو تبدیل کرتے ہیں۔

اختیارات کی دو قسمیں ہیں:

--release              # Boolean flag — either present or absent

--output=build/app     # Key-value option — name and a value
-v                     # Short flag — single hyphen, single character

یہ وہ ڈھانچہ ہے جس کی CLI کو پیروی کرنی چاہیے۔ کوڈ لکھنے سے پہلے اس کو سمجھنے کا مطلب یہ ہے کہ غلطی سے ساخت کو پیچیدہ کرنے کے بجائے جان بوجھ کر ہدایات تیار کریں۔

ڈارٹ کو ٹرمینل ان پٹ کیسے ملتا ہے۔

ڈارٹ میں، ٹول کے نام کے بعد صارف جو کچھ بھی ٹائپ کرتا ہے اس کے ذریعے پروگرام کو منتقل کیا جاتا ہے: main فنکشن:

void main(List args) {
  print(args);
}

چلائیں:

dart run bin/mytool.dart hello world --name=Seyi
# [hello, world, --name=Seyi]

کہ List args یہ صرف تاروں کی فہرست ہے۔ صارف کی طرف سے درج کردہ ہر لفظ یا جھنڈا اس فہرست کا ایک عنصر بن جاتا ہے۔ باقی سب کچھ جو آپ CLI ذیلی کمانڈز، جھنڈوں اور توثیق کے اوپر بناتے ہیں بالآخر اس فہرست پر کارروائی کرے گی۔

ڈارٹ کے بنیادی CLI تصورات

تصورات کا ایک بنیادی مجموعہ ہے جسے ہر CLI ڈویلپر کو کچھ بھی بنانے سے پہلے سمجھنا چاہیے۔ یہ عمارت کے بلاکس ہیں جن پر باقی سب کچھ ٹکی ہوئی ہے۔

stdout، stderr، اور stdin

زیادہ تر ڈویلپر استعمال کرتے ہیں۔ print() CLI کی تعمیر شروع کرتے وقت تمام آؤٹ پٹ کے لیے۔ یہ تربیت کے لیے کام کرتا ہے، لیکن پیداوار میں غلط ہے۔

ٹرمینل پروگرام میں دو الگ آؤٹ پٹ اسٹریمز ہیں:

  • stdout – صارفین کے لیے عمومی پیداوار

  • stderr – تشخیصی پیغامات اور خرابی کی پیداوار ناکامی کی نشاندہی کرتی ہے۔

import 'dart:io';

void main(List args) {
  if (args.isEmpty) {
    stderr.writeln('Error: no arguments provided');
    exit(1);
  }

  stdout.writeln('Processing: ${args[0]}');
}

ان کو الگ رکھیں کیونکہ صارف فائل کو آلودہ کرنے والی غلطیوں کے بغیر stdout کو فائل میں بھیج سکتے ہیں۔

dart run bin/tool.dart > output.txt
# Errors still appear in the terminal
# Normal output goes cleanly to the file

اوزار جیسے git، flutterاور curl سب کچھ ٹھیک کرو۔ آپ کے CLI کے لیے بھی یہی ہے۔

stdin یہ تیسرا سلسلہ ہے۔ رن ٹائم پر انٹرایکٹو طور پر صارف کے ان پٹ کو پڑھتا ہے۔

import 'dart:io';

void main() {
  stdout.write('Enter your name: ');
  final name = stdin.readLineSync();

  if (name == null || name.trim().isEmpty) {
    stderr.writeln('Error: no name provided');
    exit(1);
  }

  stdout.writeln('Hello, $name!');
}

stdout.write (بغیر ln) کرسر کو ایک ہی لائن پر رکھتا ہے، صارف کو فوری طور پر پرامپٹ کے بعد ٹائپ کرنے کی اجازت دیتا ہے۔ stdin.readLineSync() یہ اس وقت تک بلاک ہوجاتا ہے جب تک کہ صارف Enter کو دباتا ہے اور داخل کردہ اسٹرنگ کو واپس نہیں کرتا ہے۔ null جب کوئی سلسلہ غیر متوقع طور پر بند ہو جاتا ہے۔ ہمیشہ کالعدم کیسز کو ہینڈل کریں۔

باہر نکلنے کا کوڈ

تمام پروگرام مکمل ہونے پر ایگزٹ کوڈ واپس کرتے ہیں۔ اس طرح کوئی بھی اسکرپٹ یا سی آئی سسٹم جو شیلز اور ٹولز کو کال کرتا ہے جانتا ہے کہ آیا یہ کامیاب ہوا یا ناکام۔

import 'dart:io';

void main(List args) {
  if (args.isEmpty) {
    stderr.writeln('Error: please provide an argument');
    exit(1); // failure
  }

  stdout.writeln('Done');
  exit(0); // success — also the default if you don't call exit()
}

قواعد درج ذیل ہیں:

ایگزٹ کوڈ اہم ہوتا ہے جب CLI کو شیل اسکرپٹ یا GitHub ایکشن ورک فلو کے اندر سے بلایا جاتا ہے۔ ایک غیر صفر ایگزٹ کوڈ پائپ لائن کو فوری طور پر روک دیتا ہے۔ یہ بالکل وہی طرز عمل ہے جو آپ اپنے کوالٹی گیٹ یا تصدیقی مرحلے میں چاہتے ہیں۔

ماحولیاتی متغیرات

CLI صارف کے شیل میں سیٹ کردہ ماحولیاتی متغیرات کو پڑھ سکتا ہے۔

import 'dart:io';

void main() {
  final token = Platform.environment['API_TOKEN'];

  if (token == null) {
    stderr.writeln('Error: API_TOKEN environment variable is not set');
    exit(1);
  }

  stdout.writeln('Token found — proceeding...');
}

اسے اپنے ٹرمینل میں سیٹ کریں اور چلائیں:

export API_TOKEN=mytoken123
dart run bin/tool.dart
# Token found — proceeding...

یہ پیٹرن CLI ٹولز کے لیے ضروری ہے جو APIs، کلاؤڈ سروسز، یا CI ماحولیات کے ساتھ تعامل کرتے ہیں جہاں اسناد کو ہارڈ کوڈ نہیں کیا جانا چاہیے۔

فائل اور ڈائریکٹری آپریشنز

بہت سے CLI ٹولز فائل سسٹم سے پڑھتے یا لکھتے ہیں۔ ڈارٹ dart:io لائبریری آپ کو درکار ہر چیز کا احاطہ کرتی ہے۔

import 'dart:io';

void main(List args) {
  if (args.isEmpty) {
    stderr.writeln('Usage: tool ');
    exit(2);
  }

  final file = File(args[0]);

  if (!file.existsSync()) {
    stderr.writeln('Error: "${args[0]}" not found');
    exit(1);
  }

  final contents = file.readAsStringSync();
  stdout.writeln(contents);

  final output = File('output.txt');
  output.writeAsStringSync('Processed:\n$contents');
  stdout.writeln('Written to output.txt');
}

ڈائریکٹری آپریشنز:

import 'dart:io';

void main() {
  // Where the command was run from
  final cwd = Directory.current.path;
  stdout.writeln('Working directory: $cwd');

  // Create a directory relative to current location
  final dir = Directory('$cwd/generated');

  if (!dir.existsSync()) {
    dir.createSync(recursive: true);
    stdout.writeln('Created: ${dir.path}');
  } else {
    stdout.writeln('Already exists: ${dir.path}');
  }
}

کہ recursive: true ایک جھنڈا لگائیں createSync اس کا مطلب ہے تمام انٹرمیڈیٹ ڈائریکٹریز بنانا۔ mkdir -p باش میں۔

بیرونی عمل کو انجام دیں۔

سب سے طاقتور چیزوں میں سے ایک جو CLI کر سکتا ہے دوسرے پروگراموں کو کال کرنا ہے۔ آپ ڈارٹ سی ایل آئی چلا سکتے ہیں۔ git، flutter، dartیا پروگرامی طور پر کوئی شیل کمانڈ:

import 'dart:io';

void main() async {
  // Run a command and wait for it to finish
  final result = await Process.run('dart', ['pub', 'get']);

  stdout.write(result.stdout);

  if (result.exitCode != 0) {
    stderr.write(result.stderr);
    exit(result.exitCode);
  }

  stdout.writeln('Dependencies installed successfully');
}

طویل عرصے سے چلنے والی کمانڈز کے لیے جہاں آپ حقیقی وقت میں آؤٹ پٹ کو اسٹریم کرنا چاہتے ہیں:

import 'dart:io';

void main() async {
  final process = await Process.start('flutter', ['build', 'apk']);

  // Pipe output directly to the terminal in real time
  process.stdout.pipe(stdout);
  process.stderr.pipe(stderr);

  final exitCode = await process.exitCode;
  exit(exitCode);
}

Process.run – تکمیل کا انتظار کریں اور ایک ساتھ تمام آؤٹ پٹ واپس کریں۔ مختصر احکامات کے لیے استعمال کریں۔

Process.start – آپ کے آؤٹ پٹ کو آتے ہی لائیو نشر کیا جائے گا۔ طویل عرصے سے چلنے والی کمانڈز کے لیے استعمال کریں جہاں صارف کو پیش رفت کی جانچ کرنے کی ضرورت ہے۔

پلیٹ فارم کا پتہ لگانا

CLI کو مختلف طریقے سے برتاؤ کرنے کی ضرورت ہو سکتی ہے اس پر منحصر ہے کہ وہ جس آپریٹنگ سسٹم پر چل رہا ہے۔

import 'dart:io';

void main() {
  if (Platform.isWindows) {
    stdout.writeln('Running on Windows');
  } else if (Platform.isMacOS) {
    stdout.writeln('Running on macOS');
  } else if (Platform.isLinux) {
    stdout.writeln('Running on Linux');
  }

  // Useful for path handling across operating systems
  stdout.writeln(Platform.pathSeparator); // \ on Windows, / elsewhere
  stdout.writeln(Platform.operatingSystem); // 'macos', 'linux', 'windows'
}

یہ اہم ہے جب CLI فائلیں بناتا ہے، راستے حل کرتا ہے، یا شیل کمانڈز کو کال کرتا ہے جو آپریٹنگ سسٹم سے آپریٹنگ سسٹم تک مختلف ہوتی ہیں۔

CLI میں غیر مطابقت پذیر

ڈارٹ سی ایل آئی سپورٹ async/await بنیادی طور پر۔ کوئی بھی main آپ فنکشنز کو غیر مطابقت پذیر بنا سکتے ہیں۔

import 'dart:io';

void main() async {
  stdout.writeln('Starting...');

  await Future.delayed(const Duration(seconds: 1)); // simulating async work

  stdout.writeln('Done');
}

فائل I/O، HTTP درخواستیں، یا تخلیق کے عمل پر مشتمل تمام آپریشنز غیر مطابقت پذیر ہیں۔ غیر مطابقت پذیر سے واقف ہونا main ابتدائی خصوصیات—آپ ان کا استعمال جاری رکھیں گے۔

ڈارٹ سی ایل آئی پروجیکٹ سیٹ اپ

ایک نیا ڈارٹ کنسول پروجیکٹ بنائیں۔

dart create -t console my_cli_tool
cd my_cli_tool

یہ ایک صاف ڈھانچہ بناتا ہے۔

my_cli_tool/
  bin/
    my_cli_tool.dart    ← entry point
  lib/                  ← shared library code
  test/                 ← tests
  pubspec.yaml
  README.md

کہ bin/ ایک ڈائرکٹری وہ ہے جہاں قابل عمل انٹری پوائنٹ واقع ہے۔ کہ lib/ ڈائریکٹری وہ جگہ ہے جہاں آپ سب کچھ ذخیرہ کرتے ہیں: کمانڈز، یوٹیلیٹیز، ماڈلز وغیرہ۔ bin/ ہم اسے درآمد کرتے ہیں اور استعمال کرتے ہیں۔

کھلا pubspec.yaml. آپ کو شامل کرنے کی ضرورت ہے: executables پوسٹ کرنے سے پہلے بلاک کریں۔

name: my_cli_tool
description: A sample CLI tool built with Dart
version: 1.0.0

environment:
  sdk: '>=3.0.0 <4.0.0'

executables:
  my_cli_tool: my_cli_tool  # executable name: bin file name

dependencies:
  args: ^2.4.2

dev_dependencies:
  lints: ^3.0.0
  test: ^1.24.0

کہ executables بلاکس بنائے جاتے ہیں dart pub global activate my_cli_tool کام یہ ڈارٹ کو بتاتا ہے کہ کون سی اسکرپٹ شامل ہیں۔ bin/ تنصیب کے بعد، یہ ایک قابل عمل کمانڈ کے طور پر سامنے آتا ہے۔

CLI 1 - ہیلو CLI: بنیادی باتیں

یہ پہلا CLI بغیر کسی پیکج کے خالص ڈارٹ استعمال کرتا ہے۔ مقصد یہ ہے کہ بیرونی انحصار کو متعارف کرانے سے پہلے دلائل، ذیلی کمانڈ، ان پٹ کی توثیق، اور ایگزٹ کوڈز سے واقف ہو جائیں۔

درج ذیل کو تبدیل کریں: bin/my_cli_tool.dart:

import 'dart:io';

void main(List args) {
  if (args.isEmpty) {
    printHelp();
    exit(0);
  }

  final command = args[0];

  switch (command) {
    case 'greet':
      handleGreet(args.sublist(1));
    case 'time':
      handleTime();
    case 'echo':
      handleEcho(args.sublist(1));
    case 'help':
      printHelp();
    default:
      stderr.writeln('Unknown command: "$command"');
      stderr.writeln('Run "mytool help" to see available commands.');
      exit(1);
  }
}

void handleGreet(List args) {
  if (args.isEmpty) {
    stderr.writeln('Usage: mytool greet ');
    exit(2);
  }

  final name = args[0];
  stdout.writeln('Hello, $name! Welcome to your first Dart CLI.');
}

void handleTime() {
  final now = DateTime.now();
  stdout.writeln(
    'Current time: ${now.hour.toString().padLeft(2, '0')}:'
    '${now.minute.toString().padLeft(2, '0')}:'
    '${now.second.toString().padLeft(2, '0')}',
  );
}

void handleEcho(List args) {
  if (args.isEmpty) {
    stderr.writeln('Usage: mytool echo ');
    exit(2);
  }

  stdout.writeln(args.join(' '));
}

void printHelp() {
  stdout.writeln('''
mytool — a simple Dart CLI

Usage:
  mytool  [arguments]

Commands:
  greet       Greet someone by name
  time              Show the current time
  echo     Echo a message back to the terminal
  help              Show this help message

Examples:
  mytool greet Seyi
  mytool echo "Hello from the terminal"
  mytool time
  ''');
}

چلائیں:

dart run bin/my_cli_tool.dart help

dart run bin/my_cli_tool.dart greet Seyi
# Hello, Seyi! Welcome to your first Dart CLI.

dart run bin/my_cli_tool.dart time
# Current time: 14:32:10

dart run bin/my_cli_tool.dart echo "Dart CLIs are powerful"
# Dart CLIs are powerful

dart run bin/my_cli_tool.dart unknown
# Unknown command: "unknown"
# Run "mytool help" to see available commands.

یہ CLI ظاہر کرتا ہے کہ تین چیزیں ہیں جو اندرونی بنانے کے قابل ہیں۔

  1. ایک ذیلی کمانڈ آسانی سے ایک سوئچ آن کر دیتی ہے۔ args[0]. پیٹرن سادہ اور قابل توسیع ہے۔ ایک نیا پیٹرن شامل کریں۔ case ایک نیا کمانڈ شامل کرنے کے لیے

  2. args.sublist(1) بقیہ دلائل ہینڈلر کو دیں۔ جب greet استقبالیہ ['greet', 'Seyi']یہ کال کرتا ہے handleGreet(['Seyi']) - صاف اور الگ تھلگ۔

  3. ہر ایرر پاتھ میں ایک پیغام اور غیر صفر ایگزٹ کوڈ ہوتا ہے۔ صارفین ہمیشہ جانتے ہیں کہ کیا غلط ہوا اور آگے کیا کرنا ہے۔

CLI 2 — ڈارٹ_ٹوڈو: ٹرمینل ٹاسک مینیجر

یہ CLI ہے۔ args پیکیجز، JSON فائل استقامت، اور ساختی ٹرمینل آؤٹ پٹ۔ یہ CLI 1 سے کہیں زیادہ پیچیدہ ہے اور حقیقی دنیا کے نمونوں کی عکاسی کرتا ہے جسے آپ پروڈکشن ٹولز میں استعمال کریں گے۔

آرگس پیکج کا تعارف

دستی طور پر تجزیہ کریں۔ List args یہ سادہ معاملات میں کام کرتا ہے، لیکن جب آپ اس طرح کے جھنڈے شامل کرتے ہیں تو تیزی سے ٹوٹ جاتا ہے۔ --priority=highبولین اختیارات جیسے --doneیا ایک سے زیادہ اختیاری دلائل کے ساتھ ایک کمانڈ۔

کہ args پیکیج ان سب کو صاف ستھرا ہینڈل کرتا ہے۔

آپ کا pubspec.yaml:

dependencies:
  args: ^2.4.2

چلائیں:

dart pub get

کا بنیادی تصور args چاندی ArgParser. وضاحت کریں کہ CLI کیا اجازت دیتا ہے اور args خودکار طور پر تجزیہ، توثیق، اور ٹیکسٹ جنریشن میں مدد کرتا ہے۔

import 'package:args/args.dart';

void main(List arguments) {
  final parser = ArgParser()
    ..addCommand('add')
    ..addCommand('list')
    ..addFlag('help', abbr: 'h', negatable: false);

  final results = parser.parse(arguments);

  if (results['help'] as bool) {
    print(parser.usage);
    return;
  }
}

ذیلی کمانڈ کے ساتھ زیادہ پیچیدہ CLI کے لیے ہر ایک اپنے اپنے جھنڈوں کے ساتھ، استعمال کریں: ArgParser فی حکم:

final parser = ArgParser();

final addCommand = ArgParser()
  ..addOption('priority', abbr: 'p', defaultsTo: 'normal');

parser.addCommand('add', addCommand);

ڈارٹ_ٹوڈو بلڈ

ایک نیا پروجیکٹ بنائیں:

dart create -t console dart_todo
cd dart_todo

اپ ڈیٹ pubspec.yaml:

name: dart_todo
description: A terminal task manager built with Dart
version: 1.0.0

environment:
  sdk: '>=3.0.0 <4.0.0'

executables:
  dart_todo: dart_todo

dependencies:
  args: ^2.4.2

dev_dependencies:
  lints: ^3.0.0
  test: ^1.24.0

چلائیں dart pub get.

فولڈر کا ڈھانچہ بنائیں۔

dart_todo/
  bin/
    dart_todo.dart
  lib/
    models/
      task.dart
    storage/
      task_storage.dart
    commands/
      add_command.dart
      list_command.dart
      complete_command.dart
      delete_command.dart
      clear_command.dart
  pubspec.yaml

مرحلہ 1 - ورکنگ ماڈل (lib/models/task.dart)

class Task {
  final int id;
  final String title;
  final String priority;
  final bool isComplete;
  final DateTime createdAt;

  Task({
    required this.id,
    required this.title,
    required this.priority,
    this.isComplete = false,
    required this.createdAt,
  });

  Task copyWith({bool? isComplete}) {
    return Task(
      id: id,
      title: title,
      priority: priority,
      isComplete: isComplete ?? this.isComplete,
      createdAt: createdAt,
    );
  }

  Map toJson() => {
        'id': id,
        'title': title,
        'priority': priority,
        'isComplete': isComplete,
        'createdAt': createdAt.toIso8601String(),
      };

  factory Task.fromJson(Map json) => Task(
        id: json['id'] as int,
        title: json['title'] as String,
        priority: json['priority'] as String,
        isComplete: json['isComplete'] as bool,
        createdAt: DateTime.parse(json['createdAt'] as String),
      );
}

مرحلہ 2 - محفوظ کریں (lib/storage/task_storage.dart)

یہ کلاس مقامی JSON فائلوں کو پڑھنے اور لکھنے کی کارروائیوں کو ہینڈل کرتی ہے، لہذا یہ CLI عملدرآمد کے درمیان برقرار رہتی ہے۔

import 'dart:convert';
import 'dart:io';

import '../models/task.dart';

class TaskStorage {
  static final _file = File(
    '${Platform.environment['HOME'] ?? Directory.current.path}/.dart_todo.json',
  );

  static List loadAll() {
    if (!_file.existsSync()) return [];

    try {
      final content = _file.readAsStringSync();
      final List json = jsonDecode(content) as List;
      return json
          .map((e) => Task.fromJson(e as Map))
          .toList();
    } catch (_) {
      return [];
    }
  }

  static void saveAll(List tasks) {
    final json = jsonEncode(tasks.map(
    _file.writeAsStringSync(json);
  }
}

کاموں کو صارف کی ہوم ڈائرکٹری میں چھپی ہوئی JSON فائل میں محفوظ کیا جاتا ہے۔ یہ CLI ٹولز کے لیے ایک عام نمونہ ہے جس کے لیے ہلکے وزن کی مقامی استقامت کی ضرورت ہوتی ہے۔

مرحلہ 3 - کمانڈز

lib/commands/add_command.dart:

import 'dart:io';

import '../models/task.dart';
import '../storage/task_storage.dart';

void runAdd(List args, String priority) {
  if (args.isEmpty) {
    stderr.writeln('Usage: dart_todo add  [--priority=high|normal|low]');
    exit(2);
  }

  final title = args.join(' ');
  final tasks = TaskStorage.loadAll();

  final newTask = Task(
    id: tasks.isEmpty ? 1 : tasks.last.id + 1,
    title: title,
    priority: priority,
    createdAt: DateTime.now(),
  );

  tasks.add(newTask);
  TaskStorage.saveAll(tasks);

  stdout.writeln('Added task #\({newTask.id}: "\)title" [$priority]');
}

lib/commands/list_command.dart:

import 'dart:io';

import '../storage/task_storage.dart';

void runList() {
  final tasks = TaskStorage.loadAll();

  if (tasks.isEmpty) {
    stdout.writeln('No tasks yet. Add one with: dart_todo add ');
    return;
  }

  stdout.writeln('');
  stdout.writeln('  ID   Status      Priority   Title');
  stdout.writeln('  ───  ──────────  ─────────  ────────────────────────');

  for (final task in tasks) {
    final status = task.isComplete ? 'done  ' : 'pending';
    final id = task.id.toString().padRight(4);
    final priority = task.priority.padRight(9);
    stdout.writeln('  \(id \)status  \(priority  \){task.title}');
  }

  stdout.writeln('');
}

lib/commands/complete_command.dart:

import 'dart:io';

import '../storage/task_storage.dart';

void runComplete(List args) {
  if (args.isEmpty) {
    stderr.writeln('Usage: dart_todo complete ');
    exit(2);
  }

  final id = int.tryParse(args[0]);
  if (id == null) {
    stderr.writeln('Error: "${args[0]}" is not a valid task ID');
    exit(1);
  }

  final tasks = TaskStorage.loadAll();
  final index = tasks.indexWhere(

  if (index == -1) {
    stderr.writeln('Error: No task found with ID $id');
    exit(1);
  }

  if (tasks[index].isComplete) {
    stdout.writeln('Task #$id is already complete.');
    return;
  }

  tasks[index] = tasks[index].copyWith(isComplete: true);
  TaskStorage.saveAll(tasks);

  stdout.writeln('Task #\(id marked as complete: "\){tasks[index].title}"');
}

lib/commands/delete_command.dart:

import 'dart:io';

import '../storage/task_storage.dart';

void runDelete(List args) {
  if (args.isEmpty) {
    stderr.writeln('Usage: dart_todo delete ');
    exit(2);
  }

  final id = int.tryParse(args[0]);
  if (id == null) {
    stderr.writeln('Error: "${args[0]}" is not a valid task ID');
    exit(1);
  }

  final tasks = TaskStorage.loadAll();
  final index = tasks.indexWhere(

  if (index == -1) {
    stderr.writeln('Error: No task found with ID $id');
    exit(1);
  }

  final title = tasks[index].title;
  tasks.removeAt(index);
  TaskStorage.saveAll(tasks);

  stdout.writeln('Deleted task #\(id: "\)title"');
}

lib/commands/clear_command.dart:

import 'dart:io';

import '../storage/task_storage.dart';

void runClear() {
  stdout.write('Are you sure you want to delete all tasks? (y/N): ');
  final input = stdin.readLineSync()?.trim().toLowerCase();

  if (input != 'y') {
    stdout.writeln('Cancelled.');
    return;
  }

  TaskStorage.saveAll([]);
  stdout.writeln('All tasks cleared.');
}

مرحلہ 4 - انٹری پوائنٹ (bin/dart_todo.dart)

import 'dart:io';

import 'package:args/args.dart';

import '../lib/commands/add_command.dart';
import '../lib/commands/clear_command.dart';
import '../lib/commands/complete_command.dart';
import '../lib/commands/delete_command.dart';
import '../lib/commands/list_command.dart';

void main(List arguments) {
  final parser = ArgParser();

  // Add subcommand parsers
  final addParser = ArgParser()
    ..addOption(
      'priority',
      abbr: 'p',
      defaultsTo: 'normal',
      allowed: ['high', 'normal', 'low'],
      help: 'Task priority level',
    );

  parser
    ..addCommand('add', addParser)
    ..addCommand('list')
    ..addCommand('complete')
    ..addCommand('delete')
    ..addCommand('clear')
    ..addFlag('help', abbr: 'h', negatable: false, help: 'Show help');

  ArgResults results;

  try {
    results = parser.parse(arguments);
  } catch (e) {
    stderr.writeln('Error: $e');
    stderr.writeln(parser.usage);
    exit(2);
  }

  if (results['help'] as bool || results.command == null) {
    printHelp(parser);
    exit(0);
  }

  final command = results.command!;

  switch (command.name) {
    case 'add':
      runAdd(command.rest, command['priority'] as String);
    case 'list':
      runList();
    case 'complete':
      runComplete(command.rest);
    case 'delete':
      runDelete(command.rest);
    case 'clear':
      runClear();
    default:
      stderr.writeln('Unknown command: "${command.name}"');
      exit(1);
  }
}

void printHelp(ArgParser parser) {
  stdout.writeln('''
dart_todo — a terminal task manager

Usage:
  dart_todo  [arguments]

Commands:
  add         Add a new task
    -p, --priority   Priority: high, normal, low (default: normal)
  list               List all tasks
  complete <id>      Mark a task as complete
  delete <id>        Delete a task
  clear              Delete all tasks

Examples:
  dart_todo add "Write the CLI article" --priority=high
  dart_todo list
  dart_todo complete 1
  dart_todo delete 2
  dart_todo clear
  ''');
}
</id></id>

چلائیں:

dart run bin/dart_todo.dart add "Write the CLI article" --priority=high
# Added task #1: "Write the CLI article" [high]

dart run bin/dart_todo.dart add "Review PR comments"
# Added task #2: "Review PR comments" [normal]

dart run bin/dart_todo.dart list
#   ID   Status      Priority   Title
#   ───  ──────────  ─────────  ────────────────────────
#   1    ⬜ pending  high       Write the CLI article
#   2    ⬜ pending  normal     Review PR comments

dart run bin/dart_todo.dart complete 1
# Task #1 marked as complete: "Write the CLI article"

dart run bin/dart_todo.dart delete 2
# Deleted task #2: "Review PR comments"

dart_todo یہ ان نمونوں کو ظاہر کرتا ہے جو تقریباً تمام عملی CLI ٹولز کی ریڑھ کی ہڈی کی تشکیل کرتے ہیں۔ argsJSON استقامت، انٹرایکٹو پرامپٹس، سٹرکچرڈ آؤٹ پٹ، اور تمام کمانڈز کے لیے واضح ایرر ہینڈلنگ۔

CLI 3 - ڈارٹ_http: ہلکا پھلکا API درخواست ایگزیکیوٹر

یہ اس مضمون میں سب سے پیچیدہ CLI ہے اور فوری طور پر مفید ہے۔ dart_http ڈیولپرز خوبصورت پرنٹ شدہ JSON جوابات، رسپانس میٹا ڈیٹا، ہیڈر سپورٹ، اور فائلوں کے جوابات کو محفوظ کرنے کی صلاحیت کے ساتھ براہ راست ٹرمینل سے HTTP درخواستیں کر سکتے ہیں۔

dart_http get https://jsonplaceholder.typicode.com/users/1
dart_http post https://jsonplaceholder.typicode.com/posts --body='{"title":"Hello"}'
dart_http get https://jsonplaceholder.typicode.com/users --save=users.json
dart_http get https://api.example.com/me --header="Authorization: Bearer mytoken"

ڈارٹس بنانا_http

ایک پروجیکٹ بنائیں:

dart create -t console dart_http
cd dart_http

اپ ڈیٹ pubspec.yaml:

name: dart_http
description: A lightweight API request runner for the terminal
version: 1.0.0

environment:
  sdk: '>=3.0.0 <4.0.0'

executables:
  dart_http: dart_http

dependencies:
  args: ^2.4.2
  http: ^1.2.1

dev_dependencies:
  lints: ^3.0.0
  test: ^1.24.0

چلائیں dart pub get.

پروجیکٹ کی ساخت:

dart_http/
  bin/
    dart_http.dart
  lib/
    runner/
      request_runner.dart
    printer/
      response_printer.dart
    utils/
      headers_parser.dart
  pubspec.yaml
Map parseHeaders(List rawHeaders) {
  final headers = {};

  for (final header in rawHeaders) {
    final index = header.indexOf(':');
    if (index == -1) continue;

    final key = header.substring(0, index).trim();
    final value = header.substring(index + 1).trim();
    headers[key] = value;
  }

  return headers;
}

مرحلہ 2 - رسپانس پرنٹر (lib/printer/response_printer.dart)

import 'dart:convert';
import 'dart:io';

void printResponse({
  required int statusCode,
  required String body,
  required int durationMs,
  required int bodyBytes,
}) {
  final statusLabel = _statusLabel(statusCode);
  final size = _formatSize(bodyBytes);

  stdout.writeln('');
  stdout.writeln('\(statusLabel | \){durationMs}ms | $size');
  stdout.writeln('─' * 50);

  try {
    final decoded = jsonDecode(body);
    const encoder = JsonEncoder.withIndent('  ');
    stdout.writeln(encoder.convert(decoded));
  } catch (_) {
    // Not JSON — print as plain text
    stdout.writeln(body);
  }

  stdout.writeln('');
}

String _statusLabel(int code) {
  if (code >= 200 && code < 300) return '✅ $code';
  if (code >= 300 && code < 400) return '↪  $code';
  if (code >= 400 && code < 500) return '❌ $code';
  return '$code';
}

String _formatSize(int bytes) {
  if (bytes < 1024) return '${bytes}b';
  if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)}kb';
  return '${(bytes / (1024 * 1024)).toStringAsFixed(1)}mb';
}

مرحلہ 3 - درخواست کرنے والےlib/runner/request_runner.dart)

import 'dart:io';

import 'package:http/http.dart' as http;

import '../printer/response_printer.dart';

Future runRequest({
  required String method,
  required String url,
  required Map headers,
  String? body,
  String? saveToFile,
}) async {
  final uri = Uri.tryParse(url);

  if (uri == null) {
    stderr.writeln('Error: "$url" is not a valid URL');
    exit(1);
  }

  stdout.writeln('→ \({method.toUpperCase()} \)url');

  http.Response response;
  final stopwatch = Stopwatch()..start();

  try {
    switch (method.toLowerCase()) {
      case 'get':
        response = await http.get(uri, headers: headers);
      case 'post':
        response = await http.post(uri, headers: headers, body: body);
      case 'put':
        response = await http.put(uri, headers: headers, body: body);
      case 'patch':
        response = await http.patch(uri, headers: headers, body: body);
      case 'delete':
        response = await http.delete(uri, headers: headers);
      default:
        stderr.writeln('Error: unsupported method "$method"');
        exit(2);
    }
  } catch (e) {
    stderr.writeln('Error: request failed — $e');
    exit(1);
  }

  stopwatch.stop();

  printResponse(
    statusCode: response.statusCode,
    body: response.body,
    durationMs: stopwatch.elapsedMilliseconds,
    bodyBytes: response.bodyBytes.length,
  );

  if (saveToFile != null) {
    final file = File(saveToFile);
    file.writeAsStringSync(response.body);
    stdout.writeln('Response saved to $saveToFile');
  }
}

مرحلہ 4 - انٹری پوائنٹ (bin/dart_http.dart)

import 'dart:io';

import 'package:args/args.dart';

import '../lib/runner/request_runner.dart';
import '../lib/utils/headers_parser.dart';

void main(List arguments) async {
  final parser = ArgParser();

  for (final method in ['get', 'post', 'put', 'patch', 'delete']) {
    final commandParser = ArgParser()
      ..addMultiOption('header', abbr: 'H', help: 'Request header (repeatable)')
      ..addOption('body', abbr: 'b', help: 'Request body (for POST/PUT/PATCH)')
      ..addOption('save', abbr: 's', help: 'Save response body to a file');

    parser.addCommand(method, commandParser);
  }

  parser.addFlag('help', abbr: 'h', negatable: false, help: 'Show help');

  ArgResults results;

  try {
    results = parser.parse(arguments);
  } catch (e) {
    stderr.writeln('Error: $e');
    printHelp();
    exit(2);
  }

  if (results['help'] as bool || results.command == null) {
    printHelp();
    exit(0);
  }

  final command = results.command!;
  final method = command.name!;
  final rest = command.rest;

  if (rest.isEmpty) {
    stderr.writeln('Error: please provide a URL');
    stderr.writeln('Usage: dart_http $method ');
    exit(2);
  }

  final url = rest[0];
  final rawHeaders = command['header'] as List;
  final body = command['body'] as String?;
  final saveToFile = command['save'] as String?;

  final headers = parseHeaders(rawHeaders);

  // Default Content-Type for requests with a body
  if (body != null && !headers.containsKey('Content-Type')) {
    headers['Content-Type'] = 'application/json';
  }

  await runRequest(
    method: method,
    url: url,
    headers: headers,
    body: body,
    saveToFile: saveToFile,
  );
}

void printHelp() {
  stdout.writeln('''
dart_http — a lightweight API request runner

Usage:
  dart_http   [options]

Methods:
  get       Send a GET request
  post      Send a POST request
  put       Send a PUT request
  patch     Send a PATCH request
  delete    Send a DELETE request

Options:
  -H, --header    Add a request header (repeatable)
  -b, --body      Request body (JSON string)
  -s, --save      Save response body to a file
  -h, --help      Show this help message

Examples:
  dart_http get https://jsonplaceholder.typicode.com/users
  dart_http get https://api.example.com/me --header="Authorization: Bearer token"
  dart_http post https://api.example.com/posts --body=\'{"title":"Hello"}\'
  dart_http get https://api.example.com/users --save=users.json
  ''');
}

چلائیں:

dart run bin/dart_http.dart get https://jsonplaceholder.typicode.com/users/1

# → GET https://jsonplaceholder.typicode.com/users/1
# 200 | 87ms | 510b
# ──────────────────────────────────────────────────
# {
#   "id": 1,
#   "name": "Leanne Graham",
#   "username": "Bret",
#   "email": "Sincere@april.biz"
# }

dart run bin/dart_http.dart get https://jsonplaceholder.typicode.com/users --save=users.json
# → GET https://jsonplaceholder.typicode.com/users
# 200 | 143ms | 5.3kb
# ──────────────────────────────────────────────────
# [ ... ]
# Response saved to users.json

dart run bin/dart_http.dart post https://jsonplaceholder.typicode.com/posts \
  --body='{"title":"Hello from dart_http","userId":1}'
# → POST https://jsonplaceholder.typicode.com/posts
# 201 | 312ms | 72b

CLI میں رنگ اور چمک شامل کریں۔

مندرجہ بالا CLI فعال ہے، لیکن رنگوں کا استعمال ٹرمینل آؤٹ پٹ کو زیادہ پڑھنے کے قابل بنا سکتا ہے۔ کہ ansi_styles پیکیج ٹرمینلز میں متن کو رنگنے کے لیے ANSI فرار کوڈ سپورٹ فراہم کرتا ہے۔

اسے اگلی بار شامل کریں۔ pubspec.yaml:

dependencies:
  ansi_styles: ^0.3.0

اس کا استعمال:

import 'package:ansi_styles/ansi_styles.dart';

stdout.writeln(AnsiStyles.green('✅ Success'));
stdout.writeln(AnsiStyles.red('❌ Error: something went wrong'));
stdout.writeln(AnsiStyles.yellow('⚠  Warning: check your config'));
stdout.writeln(AnsiStyles.bold('dart_http — API request runner'));
stdout.writeln(AnsiStyles.cyan('→ GET https://api.example.com/users'));

جان بوجھ کر اور مستقل طور پر رنگ لگائیں۔

  • سبز - کامیابی کی حیثیت، کام مکمل

  • سرخ - غلطیاں اور ناکامیاں

  • پیلا - انتباہ اور غیر مسدود مسائل

  • فیروزی - معلومات کی پیداوار، URL، راستہ

  • بہادر - ہیڈر، ٹول کا نام، اہم اقدار

ہر چیز کو رنگ نہ دیں۔ رنگ جب ہر جگہ ہوتے ہیں تو اپنا معنی کھو دیتے ہیں۔ آپ اسے صارفین کی توجہ اس طرف مبذول کرنے کے لیے استعمال کر سکتے ہیں کہ اصل میں کیا اہم ہے۔

CLI ٹولز قابل آزمائش ہیں اور ان کی جانچ ہونی چاہیے۔ سب سے قابل اعتماد نقطہ نظر ٹرمینل آؤٹ پٹ فارمیٹ کے بجائے براہ راست طرز عمل کی جانچ کرنا ہے۔

شامل کریں test اگر آپ کے پاس پہلے سے ڈویلپر انحصار نہیں ہے:

dev_dependencies:
  test: ^1.24.0

ٹیسٹ کمانڈ منطق:

import 'package:test/test.dart';

import '../lib/models/task.dart';

void main() {
  group('Task model', () {
    test('copyWith updates isComplete correctly', () {
      final task = Task(
        id: 1,
        title: 'Write tests',
        priority: 'high',
        createdAt: DateTime.now(),
      );

      final completed = task.copyWith(isComplete: true);

      expect(completed.isComplete, isTrue);
      expect(completed.title, equals('Write tests'));
      expect(completed.id, equals(1));
    });

    test('toJson and fromJson round-trips correctly', () {
      final task = Task(
        id: 2,
        title: 'Ship the tool',
        priority: 'normal',
        createdAt: DateTime.parse('2025-01-01T00:00:00.000'),
      );

      final json = task.toJson();
      final restored = Task.fromJson(json);

      expect(restored.id, equals(task.id));
      expect(restored.title, equals(task.title));
      expect(restored.priority, equals(task.priority));
    });
  });
}

ہیڈر پارسر ٹیسٹ:

import 'package:test/test.dart';

import '../lib/utils/headers_parser.dart';

void main() {
  group('parseHeaders', () {
    test('parses a single header correctly', () {
      final result = parseHeaders(['Authorization: Bearer mytoken']);
      expect(result['Authorization'], equals('Bearer mytoken'));
    });

    test('parses multiple headers', () {
      final result = parseHeaders([
        'Authorization: Bearer token',
        'Accept: application/json',
      ]);
      expect(result.length, equals(2));
      expect(result['Accept'], equals('application/json'));
    });

    test('ignores malformed headers without a colon', () {
      final result = parseHeaders(['malformed-header']);
      expect(result.isEmpty, isTrue);
    });
  });
}

ٹیسٹ چلائیں۔

dart test

CLI تعیناتی اور تعیناتی۔

CLI ٹول بنانا آدھا کام ہے۔ اسے ڈویلپرز کے ہاتھوں میں ڈالنا دوسرا نصف ہے۔ تعیناتی کے پانچ راستے دستیاب ہیں، ہر ایک مختلف استعمال کے معاملے کے لیے موزوں ہے۔

موڈ 1: pub.dev — عوامی پیکیج کی تقسیم

pub.dev پر شائع کر کے، Dart اور Flutter کمیونٹی میں کوئی بھی شخص آپ کے ٹولز کو ایک کمانڈ سے انسٹال کر سکتا ہے۔

اپنا پیکج تیار کریں:

آپ کا pubspec.yaml آپ کو مکمل کرنا ہوگا:

name: dart_http
description: A lightweight API request runner for Dart developers.
version: 1.0.0
homepage: https://github.com/yourname/dart_http

environment:
  sdk: '>=3.0.0 <4.0.0'

executables:
  dart_http: dart_http

کہ executables بلاکس اہم ہیں۔ یہ آپ کو بتاتا ہے کہ pub.dev میں کون سی اسکرپٹ ہیں۔ bin/ یہ ایک قابل عمل کمانڈ کے طور پر سامنے آیا ہے۔

آپ کو بھی ضرورت ہو گی:

  • README.md - ٹول کیا کرتا ہے، اسے کیسے انسٹال کیا جائے، استعمال کی مثالیں۔

  • CHANGELOG.md - ورژن کی تاریخ

  • LICENSE - اوپن سورس لائسنس (MIT معیاری ہے)

پوسٹ کرنے سے پہلے چیک کریں۔

dart pub publish --dry-run

اصل میں شائع کیے بغیر تمام توثیق چلاتا ہے۔ براہ کرم جاری رکھنے سے پہلے وارننگ کو درست کریں۔

میں پوسٹ کیا گیا:

dart pub publish

آپ کو اپنے pub.dev اکاؤنٹ سے تصدیق کرنے کے لیے کہا جائے گا۔ ایک بار شائع ہونے کے بعد، ٹول عالمی سطح پر دستیاب ہوگا۔

dart pub global activate dart_http
dart_http get https://api.example.com/users

موڈ 2: لوکل پاتھ کو فعال کریں۔

ٹیم کے اندرونی ٹولز کے لیے جنہیں آپ عوامی طور پر شائع نہیں کرنا چاہتے، انہیں براہ راست مقامی یا کلون شدہ ریپوزٹری پر فعال کریں۔

dart pub global activate --source path /path/to/dart_http

ٹیم کا ہر ڈویلپر ریپوزٹری کو کلون کرتا ہے اور اس کمانڈ کو ایک بار چلاتا ہے۔ اس کے بعد یہ ٹول آپ کے ٹرمینل میں pub.dev کی اشاعت کی ضرورت کے بغیر عالمی سطح پر دستیاب ہوگا۔

یہ اس کے لیے موزوں تعیناتی موڈ ہے:

موڈ 3: GitHub ریلیز کے ذریعے مرتب کردہ بائنریز

ڈارٹ کو خود ساختہ مقامی ایگزیکیوٹیبل میں مرتب کیا جاسکتا ہے۔ ٹارگٹ سسٹم پر Dart SDK کی ضرورت نہیں ہے۔ یہ ٹولز کو ڈارٹ ماحولیاتی نظام سے باہر کے ڈویلپرز کے لیے قابل رسائی بناتا ہے۔

plait:

# macOS
dart compile exe bin/dart_http.dart -o dist/dart_http-macos

# Linux
dart compile exe bin/dart_http.dart -o dist/dart_http-linux

# Windows
dart compile exe bin/dart_http.dart -o dist/dart_http-windows.exe

مرتب شدہ بائنریز مکمل طور پر خود ساختہ ہیں۔ اسے کسی بھی کمپیوٹر پر کاپی کر کے چلائیں۔ آپ کو ڈارٹ انسٹال کرنے کی ضرورت نہیں ہے۔

GitHub ایکشن کے ساتھ خودکار:

بنانا .github/workflows/release.yml:

name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v3

      - uses: dart-lang/setup-dart@v1
        with:
          sdk: stable

      - name: Install dependencies
        run: dart pub get

      - name: Compile binary
        run: |
          mkdir -p dist
          dart compile exe bin/dart_http.dart -o dist/dart_http-${{ runner.os }}

      - name: Upload binary to release
        uses: softprops/action-gh-release@v1
        with:
          files: dist/dart_http-${{ runner.os }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

ورژن ٹیگ (v1.0.0)، GitHub ایکشن تینوں پلیٹ فارمز کے لیے بائنریز کو مرتب کرتا ہے اور خود بخود انہیں GitHub ریلیز سے جوڑ دیتا ہے۔

انسٹالیشن اسکرپٹ لکھیں:

#!/usr/bin/env bash
set -euo pipefail

VERSION="1.0.0"
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
BINARY="dart_http-$OS"
INSTALL_DIR="/usr/local/bin"

curl -L "https://github.com/yourname/dart_http/releases/download/v\(VERSION/\)BINARY" \
  -o "$INSTALL_DIR/dart_http"

chmod +x "$INSTALL_DIR/dart_http"
echo "dart_http installed successfully"

ڈویلپرز استعمال کرتے ہوئے انسٹال کرتے ہیں:

curl -fsSL https://raw.githubusercontent.com/yourname/dart_http/main/install.sh | bash

موڈ 4: ہومبریو ٹیب

ہومبریو میک او ایس پر معیاری پیکیج مینیجر ہے اور لینکس پر وسیع پیمانے پر استعمال ہوتا ہے۔ ہومبریو ٹیب آپ کو ٹولز انسٹال کرنے کی اجازت دیتا ہے۔ brew install - یہ میکوس ڈویلپرز کے لیے سب سے زیادہ مانوس انسٹالیشن پیٹرن ہے۔

ایک ٹیب اسٹور بنائیں۔

نام کا ایک نیا GitHub ذخیرہ بنائیں: homebrew-tools (وہ homebrew- ہومبریو کے نام سازی کنونشن کے ذریعہ سابقہ ​​کی ضرورت ہے)۔

فارمولا لکھیں:

بنانا Formula/dart_http.rb اس ذخیرہ سے:

class DartHttp < Formula
  desc "A lightweight API request runner for the terminal"
  homepage "https://github.com/yourname/dart_http"
  version "1.0.0"

  on_macos do
    url "https://github.com/yourname/dart_http/releases/download/v1.0.0/dart_http-macOS"
    sha256 "YOUR_SHA256_HASH_HERE"
  end

  on_linux do
    url "https://github.com/yourname/dart_http/releases/download/v1.0.0/dart_http-Linux"
    sha256 "YOUR_SHA256_HASH_HERE"
  end

  def install
    bin.install "dart_http-#{OS.mac? ? 'macOS' : 'Linux'}" => "dart_http"
  end

  test do
    system "#{bin}/dart_http", "--help"
  end
end

ہر بائنری کے لیے SHA256 ہیش بنائیں۔

shasum -a 256 dist/dart_http-macOS

ٹیب سے انسٹال کریں:

brew tap yourname/tools
brew install dart_http

نیا ورژن جاری ہونے پر براہ کرم اپ ڈیٹ کریں۔ url اور sha256 فارمولے کی قدریں درج کریں اور اپنی تبدیلیوں کو آگے بڑھائیں۔ صارف کے ذریعہ چلایا جاتا ہے۔ brew upgrade dart_http اپ ڈیٹ کریں۔

موڈ 5: ڈوکر

ڈوکر کی تعیناتیاں CI ماحولیات، کنٹینرز کو معیاری بنانے والی ٹیموں، یا پیچیدہ انحصار والے ٹولز کے لیے بہترین ہیں۔

ایک ڈاکر فائل لکھیں۔

FROM dart:stable AS build

WORKDIR /app
COPY pubspec.* ./
RUN dart pub get

COPY . .
RUN dart compile exe bin/dart_http.dart -o /app/dart_http

FROM debian:stable-slim
COPY --from=build /app/dart_http /usr/local/bin/dart_http

ENTRYPOINT ["dart_http"]

یہ ایک کثیر مرحلے کی تعمیر کا استعمال کرتا ہے. پہلا مرحلہ ڈارٹ SDK امیج کا استعمال کرتے ہوئے بائنریز کو مرتب کرتا ہے، اور دوسرا مرحلہ صرف بائنریز کو کم سے کم ڈیبین امیج پر کاپی کرتا ہے۔ حتمی تصویر میں Dart SDK نہیں ہے، صرف مرتب شدہ بائنری ہے۔

بنائیں اور چلائیں:

docker build -t dart_http .
docker run dart_http get https://jsonplaceholder.typicode.com/users/1

Docker Hub پر شائع کریں:

docker tag dart_http yourname/dart_http:1.0.0
docker push yourname/dart_http:1.0.0

اس کے بعد صارف مقامی طور پر کچھ بھی انسٹال کیے بغیر اس ٹول کو چلا سکتے ہیں۔

docker run yourname/dart_http get https://api.example.com/users

صحیح تعیناتی موڈ کا انتخاب کریں۔

طریقہ کے لیے بہترین موزوں ہے۔ Dart SDK کی ضرورت ہے۔
pub.dev پبلک ڈارٹ/فلٹر ڈویلپر ٹولز ہاں
مقامی راستے کو فعال کریں۔ اندرونی ٹیم ٹولز، پری ریلیز کی تعمیر ہاں
مرتب شدہ بائنری زبان-ایگنوسٹک ٹولز، وسیع پیمانے پر اپنانے نہیں
ہومبریو ٹیب macOS/Linux ڈویلپر ٹولز نہیں
ڈاکر CI ماحول، پیچیدہ انحصار نہیں

زیادہ تر ٹولز کے لیے میری اصل سفارشات یہ ہیں:

  • سے شروع کریں۔ pub.dev اگر آپ کے سامعین ڈارٹ ڈویلپر ہیں۔

  • شامل کریں مرتب شدہ بائنریز + GitHub ریلیز اگر آپ وسیع تر اپنانا چاہتے ہیں۔

  • اضافہ ہومبریو ٹیب جب macOS ڈویلپرز مطالبہ کرنا شروع کرتے ہیں۔

  • استعمال کریں ڈاکر صرف اس صورت میں جب یہ پہلے سے ہی آپ کی ٹیم کے ورک فلو کا حصہ ہو۔

نتیجہ

ہم یہ سمجھنے سے آگے نکل گئے کہ CLI کیا ہے تین بتدریج پیچیدہ ٹولز بنانا اور انہیں پانچ مختلف چینلز پر تعینات کرنا۔

بنیادی مہارتیں - args، stdin، stdout، stderrایگزٹ کوڈ، فائل I/O، اور پروسیس تخلیق وہی اجزاء ہیں جیسے ٹولز: flutter، gitاور dart اپنے طور پر بنایا گیا ہے۔ باقی سب کچھ کنفیگریشن ہے۔

تین CLIs جو ہم نے بنائے ہیں (ہیلو CLI، dart_todoاور dart_http) ہر ایک ایک نئی پرت متعارف کراتا ہے: خام ڈارٹ کی بنیادی باتیں۔ args JSON استقامت اور حقیقی HTTP تعامل کے ساتھ ایک پیکیج۔ تعیناتی سیکشن آپ کو ایک واضح راستہ فراہم کرتا ہے کہ آپ اسے ڈویلپرز کے سامنے آشکار کریں جو آپ جو بھی تعمیر کریں گے اسے استعمال کریں گے۔

ڈارٹ CLI کی ترقی کے لیے ایک طاقتور زبان ہے۔ مضبوط ٹائپنگ، غیر مطابقت پذیر سپورٹ، مقامی تالیف، اور pub.dev ماحولیاتی نظام اسے موبائل ایپس کے ساتھ ساتھ ڈویلپر ٹولز بنانے کے لیے بہترین انتخاب بناتا ہے۔

اگلا مرحلہ کچھ ایسا بنانا ہے جو آپ یا آپ کی ٹیم کے لیے ایک حقیقی مسئلہ حل کرے اور اسے جاری کرے۔

کوڈنگ کا مزہ لیں !!

اوپر تک سکرول کریں۔