Skip to content

ui5:doctor ​

since 5.0 php artisan ui5:doctor checks your checkout and tells you what is wrong and how to fix it.

It reads the UI5 projects your modules resolve, their manifests and yamls, the resource bundles and .ui5-sources.php. It never writes a file and never touches a database, so you can run it any time: right after a scaffold, when an app misbehaves, and in CI before a tag.

bash
php artisan ui5:doctor

Reading the report ​

Findings are grouped by severity. Each line names the rule, the subject (a namespace, a file or a key), what is wrong, and after the dash, the fix:

text
Errors
  core.W1 com.acme.offers: sap.f is in webapp/manifest.json but not in ui5-dist.yaml — add `- name: sap.f` under framework.libraries in ui5-dist.yaml

Warnings
  core.W4 ../ui5-offers/webapp/i18n/i18n_de.properties: save is missing; other locales of the bundle have it — add save to i18n_de.properties

Notices
  core.W2 com.acme.offers: resolved by the workspace strategy from ../ui5-offers

Skipped
  core.W3 skipped: no workspace or self-contained app with a Component.ts or Component.js

5 rules run: 1 error, 1 warning, 1 notice, 1 skipped.
SeverityMeansExit code
ErrorSomething is broken now.1
WarningA fault that will bite later, such as the wrong language on screen.0
NoticeWorth knowing while you debug. A notice may come without a fix.0
SkippedThe rule could not look, and says why. A skip is never a pass.0

The command runs every rule, every time. There are no options to pick rules.

What Core checks ​

RuleChecksSeverity
core.W1Every sap.* library in an app's webapp/manifest.json is declared in its ui5.yaml, and in ui5-dist.yaml and ui5-coverage.yaml where those exist. The two placesError
core.W2Every entry in .ui5-sources.php points to a UI5 project (ui5.yaml, and webapp/manifest.json for an app), and the file stays out of git. For each project, a notice names the strategy that resolved it. Workspace linksError, Notice
core.W3An app's component calls LaravelUi5.init(this). Without it the shell does not mount, and writes miss their CSRF handshake. FrontendError
core.W4A key in one locale file of a bundle (i18n_en, i18n_de, …) is in all of them. Keys missing from the base file are one notice per file, since a minimal base bundle can be deliberate.Warning, Notice
core.W5Bundles hold no lone surrogate (shown as �), no key whose {N} placeholders differ between locales, and no key defined twice in one file. A file the server cannot read is an error.Warning, Error

Core reads the projects you edit: workspace apps and libraries, and self-contained apps. A package installed through Composer carries built resources. Its fix belongs in its source project.

The two places ​

A UI5 library has to be declared in two places. manifest.json drives what the app loads. ui5.yaml, together with the sibling ui5-dist.yaml and ui5-coverage.yaml, drives what the tooling resolves. A library declared in the manifest only fails at runtime with failed to load 'library.js', and the message does not say which declaration is missing. core.W1 does: it names the library and the yaml.

The Easy UI5 generator scaffolds sap.ui.core and sap.m only. When your app renders a dashboard, add sap.f, and sap.ui.integration for integration cards, in both places (Ui5App).

.ui5-sources.php maps a module to the UI5 project on your disk (the workspace strategy). An entry whose directory is missing does not fail. The module quietly resolves through its package instead, and serves its built resources rather than your source. core.W2 reports that entry.

The file never belongs in git. It describes this checkout. In another one, a colleague's or CI's, its links point nowhere. If the module has no built resources to fall back to, the application then cannot build its registry at all. core.W2 reports a checked-in file as an error and a file that is neither checked in nor ignored as a warning. ui5:app and ui5:lib add it to .gitignore when they create it; in an older host, add it yourself:

text
.ui5-sources.php

When the registry does not load, ui5:doctor still runs. It reports the failure as an error of its own and runs every rule without projects. The file rules skip, and core.W2 still reads .ui5-sources.php, which is where the cause usually is.

In CI ​

Run it as a step before you tag. A finding with severity Error fails the step:

bash
php artisan ui5:doctor

Keep .ui5-sources.php out of the repository, as above. A pipeline without it has no workspace links, so core.W2 has nothing to check, and the file rules skip the packaged modules.

Adding a rule ​

A package, or your host, adds rules through the container. A rule implements LaravelUi5\Core\Diagnosis\Contracts\DoctorRuleInterface and is tagged with it:

php
use LaravelUi5\Core\Diagnosis\Contracts\DoctorRuleInterface;
use LaravelUi5\Core\Diagnosis\DiagnosisContext;
use LaravelUi5\Core\Diagnosis\Finding;
use LaravelUi5\Core\Diagnosis\Outcome;

final class VhostVersionRule implements DoctorRuleInterface
{
    public function id(): string
    {
        return 'host.W1';
    }

    public function description(): string
    {
        return "Each app's version matches the @version in the dev vhost.";
    }

    public function run(DiagnosisContext $context): Outcome
    {
        if (!is_file(base_path('etc/vhost.conf'))) {
            return Outcome::skip('no etc/vhost.conf');
        }

        $findings = [];
        foreach ($context->apps() as $namespace => $project) {
            // compare $project->root->getVersion() with the vhost's ProxyPass lines …
        }

        return Outcome::findings(...$findings);
    }
}
php
// AppServiceProvider::register()
$this->app->tag([VhostVersionRule::class], DoctorRuleInterface::class);
  • The id is <owner>.W<n>: core, sdk, foundation and platform belong to the LaravelUi5 packages, host to your application. The command refuses a malformed or duplicate id before the first rule runs.
  • The context carries the registered projects, walked once: projects, apps(), libraries(). Each one gives its namespace(), its root artifact, strategyName(), projectRoot() (the directory with ui5.yaml, for a workspace project) and sourcePath(). Anything else a rule needs comes through its constructor.
  • The outcome is Outcome::pass(), Outcome::finding($f), Outcome::findings(...$f) or Outcome::skip($reason). A finding is built with Finding::error() or Finding::warning(), which both require a fix, or with Finding::notice(), where the fix is optional.
  • Tag it unconditionally. A rule that does not apply to a system answers pass() or skip().
  • Read only. A rule writes no file and touches no database. A check that needs the database is an installation check, not a diagnosis of the checkout.