search ESC

Searching…

No results for "".

Type at least 2 characters to search.

Docs

plugin:install

Register a third-party artisan plugin into the consumer project. After adding a package to pubspec.yaml and running flutter pub get, plugin:install wires the plugin's ArtisanServiceProvider without requiring manual edits to bin/artisan.dart or lib/app/_plugins.g.dart.


Basic Usage

dart run fluttersdk_artisan plugin:install 

is the plugin's pubspec package name (e.g. magic_logger). The package must be listed in pubspec.yaml and resolved via flutter pub get before this command runs.

Pre-flight checks (lib/src/commands/plugin_install_command.dart:197-241): bin/artisan.dart must exist, the package must appear in pubspec.yaml, and .dart_tool/package_config.json must contain the package name. Any failure exits with code 1 before any write is attempted.


Synopsis

Full signature as declared in lib/src/commands/plugin_install_command.dart:67:

plugin:install
  {name                  : Plugin pubspec package name (e.g. magic_logger)}
  {--provider=           : Override the auto-derived provider class name}
  {--bootstrap-command=  : Plugin install sub-command to chain after registration}
  {--use-yaml-only       : Fail if install.yaml not found instead of falling back to legacy injection}
  {--force               : Bypass conflict detection (inherited from ArtisanInstallCommand)}
  {--dry-run             : Print staged ops without writing (inherited)}
  {--non-interactive     : Skip prompts, use defaults (inherited)}
  {--no-bootstrap        : Skip post-install bootstrap hint message (inherited)}

The four inherited flags come from ArtisanInstallCommand.baseFlags and are available on every install command, not only plugin:install.

Flag Effect
--provider=ClassName Overrides ArtisanProvider convention (Mode 3 only).
--bootstrap-command=cmd Overrides the post-registration bootstrap hint (e.g. logger:install).
--use-yaml-only Error out when no install.yaml found; no Mode 2 or Mode 3 fallback.
--force Bypass conflict detection; overwrite user-modified files.
--dry-run Print every staged op without touching disk.
--non-interactive Skip interactive prompts; use defaults. Required in non-TTY environments.
--no-bootstrap Suppress the post-install bootstrap hint message.

Three Routing Modes

The core of handle dispatches through three distinct code paths (lib/src/commands/plugin_install_command.dart:130-181). The selection logic runs in strict priority order: Mode 1 wins when an install.yaml manifest is present; Mode 2 wins when the canonical scaffold barrel exists; Mode 3 is the final fallback.

Mode 1: install.yaml Manifest Flow

Condition: resolveInstallYaml(name) returns a non-null path. The resolver checks /install.yaml first, then /assets/install.yaml (lib/src/commands/plugin_install_command.dart:110-127).

ManifestParser.parseFile parses the YAML into an InstallManifest. ManifestInstaller stages InstallOperation objects on an InstallTransaction. InstallTransaction.commit writes every file to a .tmp path first; on full success, all .tmp files are renamed atomically. Any .tmp write failure deletes all staged temps and returns Error(rolledBack: true). On Success, the plugin entry is written to .artisan/plugins.json and PluginsRefreshCommand regenerates lib/app/_plugins.g.dart in-process.

Preferred authoring path: declarative, conflict-checked, dry-run-capable, and partially reversible via plugin:uninstall (V1 limitations; see Reversibility).

Mode 2: Canonical Scaffold Fast Path

Condition: No install.yaml found AND lib/app/_plugins.g.dart exists at the project root (lib/src/commands/plugin_install_command.dart:172-178).

_registerArtisanProvider runs directly: writes a PluginEntry to .artisan/plugins.json and regenerates _plugins.g.dart. No bin/artisan.dart edit occurs. Covers plugins without a manifest whose consumers used consumer:scaffold. Naming convention: my_plugin maps to package:my_plugin/cli.dart and class MyPluginArtisanProvider.

Mode 3: Legacy bin/artisan.dart Injection

Condition: No install.yaml found AND lib/app/_plugins.g.dart does not exist (lib/src/commands/plugin_install_command.dart:180).

  1. import 'package:/cli.dart'; is appended to bin/artisan.dart via ConfigEditor.addImportToFile (idempotent; skips when already present).
  2. registry.registerProvider(()); is inserted after the registry.registerAll(auto.commands, ...) anchor, or before the ArtisanApplication construction line when the anchor is absent.
  3. Provider class defaults to ArtisanProvider; override with --provider=ClassName.

Retained for backward compatibility with plugins predating the install.yaml schema. Pass --use-yaml-only to error out instead of reaching this fallback.


Examples

1. Manifest-based plugin install (Mode 1)

flutter pub add magic_logger
dart run fluttersdk_artisan plugin:install magic_logger

Output:

Success: applied 4 ops; record at .artisan/installed/magic_logger.json
Bootstrap with: artisan logger:install

2. Canonical scaffold, no install.yaml (Mode 2)

flutter pub add awesome_plugin
dart run fluttersdk_artisan plugin:install awesome_plugin

Output:

Registered "awesome_plugin" via canonical scaffold (no install.yaml needed).

3. Dry-run preview

dart run fluttersdk_artisan plugin:install magic_logger --dry-run

Output:

DryRun: previewed 4 ops; no changes written
  WriteFile           lib/config/logger.dart
  InjectImport        lib/main.dart
  InjectAfterPattern  lib/app/providers/app_service_provider.dart
  AddPubspecAsset     assets/logging/

Idempotency

Re-running plugin:install for the same plugin is safe in all three modes. PluginsRegistryFile.addPlugin replaces by name so .artisan/plugins.json always contains exactly one entry per plugin. ConfigEditor.addImportToFile and insertCodeAfterPattern skip injection when the target content is already present. Mode 3's registerProvider line is also checked before insertion (unless --force is passed).

All InstallTransaction file writes use .tmp + atomic rename: concurrent readers never observe partial state. A mid-commit .tmp write failure deletes all staged temps and returns Error(rolledBack: true) before any rename occurs.


Reversibility

plugin:uninstall is the complement to plugin:install. A Mode 1 manifest commit writes .artisan/installed/.json listing every op and content hash. V1 reversibility is partial: WriteFile ops are fully reversed (tamper-check then delete); InjectImport, InjectBeforePattern, and InjectAfterPattern ops are logged as [skipped] (no anchor-bracketed markers in V1; operator must reverse by hand). Mode 2 and Mode 3 installs write no record, so plugin:uninstall cannot auto-reverse them. Full bidirectional reversibility is planned for V1.1.


  • plugin:uninstall: removes a plugin registered via plugin:install, using the .artisan/installed/.json record.
  • plugins:refresh: regenerates lib/app/_plugins.g.dart from .artisan/plugins.json when the codegen barrel drifts out of sync.
  • install.yaml schema: full reference for the declarative manifest format consumed by Mode 1.