@@ -446,16 +446,53 @@ A handler resolves what it needs itself, at the top of its own body:
446446export default defineCommand ({
447447 name: " widget|add" ,
448448 arguments: " any" ,
449+ providers: [provideProject ()],
449450 async run(ctx ) {
450451 const widgets = inject (WidgetService );
451452 const projectData = inject (ProjectData );
452453
453- projectData .initializeProjectData ();
454- await widgets .add (ctx .args );
454+ await widgets .add (ctx .args , projectData );
455455 },
456456});
457457```
458458
459+ ### Declaring what the command needs: ` providers ` and preconditions
460+
461+ A definition may carry ` providers ` , added to each invocation's own injector
462+ next to the context, so a factory or class among them can inject the
463+ invocation and is built once per invocation. One token in that list is
464+ special: ` COMMAND_PRECONDITIONS ` is a multi token, and every
465+ ` { provide: COMMAND_PRECONDITIONS, multi: true, useValue: check } ` contributes
466+ a ** precondition** — a check on the environment the command runs in, as
467+ opposed to ` canExecute ` , which judges the arguments. Preconditions run when
468+ the invocation opens, in declaration order, before ` setup ` and before the
469+ arguments policy, inside the injection context, and a throw fails the
470+ invocation. That fixed order is the point: being outside a project is what a
471+ bad invocation reports first.
472+
473+ The precondition every project command declares comes from a helper:
474+
475+ ``` ts
476+ import { provideProject } from " ../command-base" ;
477+
478+ export default defineCommand ({
479+ name: " platform|clean" ,
480+ providers: [provideProject ()],
481+ run(ctx ) {
482+ const projectData = inject (ProjectData ); // the project the command line names
483+ },
484+ });
485+ ```
486+
487+ ` provideProject() ` resolves the project from ` --path ` or the working
488+ directory and fails the invocation with the usual "no project found" error
489+ when there is none. A command that does not declare it — ` doctor ` , ` create ` ,
490+ the ` device ` family — pays nothing, and a command that needs the project only
491+ when it is there, like ` clean ` , resolves it itself behind its own check. Never
492+ call ` initializeProjectData() ` from a command; declare the provider. A plugin
493+ adds its own preconditions the same way, with its own helper returning a
494+ multi provider for the token.
495+
459496The injection context is synchronous, so the ` inject() ` calls belong ** above
460497the first ` await ` ** — see [ Injection, and the first
461498` await ` ] ( #injection-and-the-first-await ) . Resolve everything the handler needs
@@ -552,16 +589,12 @@ export class PlatformCleanCommand extends Command({
552589 description: " Removes and adds again the selected platform." ,
553590 options: { frameworkPath: stringOption () },
554591 arguments: " any" ,
592+ providers: [provideProject ()],
555593}) {
556594 private $platformCommandHelper = inject <IPlatformCommandHelper >(
557595 " platformCommandHelper" ,
558596 );
559- private $projectData = inject <IProjectData >(" projectData" );
560-
561- constructor () {
562- super ();
563- this .$projectData .initializeProjectData ();
564- }
597+ private $projectData = inject (ProjectData );
565598
566599 public async run(): Promise <void > {
567600 await this .$platformCommandHelper .cleanPlatforms (
@@ -625,16 +658,14 @@ across invocations, and resolves nothing outside a running one.
625658read as ` this.$x ` :
626659
627660``` ts
628- export class PlatformAddCommand extends Command ({ name: " platform|add" }) {
629- private $projectData = inject <IProjectData >(" projectData" );
661+ export class PlatformAddCommand extends Command ({
662+ name: " platform|add" ,
663+ providers: [provideProject ()],
664+ }) {
665+ private $projectData = inject (ProjectData );
630666 private $platformHelper = inject <IPlatformCommandHelper >(
631667 " platformCommandHelper" ,
632668 );
633-
634- constructor () {
635- super ();
636- this .$projectData .initializeProjectData ();
637- }
638669 // ...
639670}
640671```
@@ -700,8 +731,8 @@ the registry. It claims every name the definition declares, through the
700731is built by a factory on first resolution and cached.
701732
702733Pass providers as the second argument to add them to each invocation's child
703- injector, the one ` ctx.injector ` names — how a definition is parameterized per
704- registration:
734+ injector, the one ` ctx.injector ` names, next to the definition's own
735+ ` providers ` — how a definition is parameterized per registration:
705736
706737``` ts
707738for (const [name, platform] of buildCommandPlatforms ) {
0 commit comments