{}OPF
DocsReferenceView source

OPF lint for humans and agents

Core 0.10.0 adds the browser-safe @openpresentation/opf/lint entrypoint, and CLI 0.8.0 adds opf lint. Earlier versions do not include them; check opf --help before asking an installed CLI to lint.

sh
node packages/cli/dist/index.js lint deck.opf.json
node packages/cli/dist/index.js lint deck.opf.json --config brand-lint.json --strict

Lint is read-only and local. It returns JSON diagnostics with stable rule IDs, severity, JSON Pointer paths, original-source UTF-16 ranges, one-based line/column, explanations, contextual suggestions, and schema/catalog definitions. The CLI includes the original file SHA-256 and the bundled core version. A supplied configuration file has its own path and hash. No AI call, account, remote catalog fetch, source normalization, or automatic fix is involved.

CheckBehavior
Strict JSON syntaxReports malformed tokens, comments and trailing commas with source ranges
Duplicate JSON keysReports both escaped and literal spellings of the same key; an earlier value cannot silently disappear into JSON.parse
OPF schema and semantic constraintsRetains the full validator issue, including all union alternatives, and points to the actual schema
Catalog referencesUses document records over supplied loaded records over built-ins; unknown IDs are advisory, including engine-defined layouts
Catalog definitionsValidates supplied and inline records; rejects duplicate IDs within one catalog and reports invalid overrides
Asset referencesReports missing document registry IDs and cyclic asset: references; does not fetch resource bytes
Explicit contractsReports existing fields outside the allowed values in a host-supplied policy

Free-form audience/purpose descriptions and arbitrary extension data do not become catalog references because of their spelling. An inline custom tone/narrative remains distinct from a string catalog reference. External catalog sources remain visible as informational diagnostics; URL and pkg: records are not resolved by this local lint pass. Suggestions name records actually present in the supplied context and never silently replace authored values.

Language string shorthands with BCP-47 syntax, including regional, extended, private-use and grandfathered forms, do not require a language catalog record. Their spelling is preserved. This is syntax recognition, not IANA registry validation; an explicit language.id is still a catalog reference, and the existing OPF en-UK error remains enforced. Custom inline narrative IDs remain valid with or without a beats array.

valid means no lint errors. schemaValid separately reports structural validation, and is null when malformed JSON prevented validation. Exit code 0 means no lint errors; 1 means lint errors, or warnings with --strict; 2 means a usage, configuration or I/O failure. The existing opf validate command retains its existing report and exit behavior.

Catalog context and design contracts

An explicit local JSON file may contain catalogs and contracts. It is host configuration, separate from the OPF document. Fields under document extensions are data and cannot install lint policy.

json
{
  "catalogs": {
    "layouts": [
      {"id":"partner-title","name":"Partner title","placeholders":[{"type":"title"}]}
    ]
  },
  "contracts": [
    {
      "path":"/slides/*/layout",
      "allowedValues":["partner-title","text-1x"],
      "message":"Use the brand layouts {{allowed}} at {{path}}. See {{file}}.",
      "documentation":"brand-guide.md#layouts",
      "severity":"error"
    }
  ]
}

Contract paths are JSON Pointer patterns: ~0 escapes ~, ~1 escapes /, and a whole * segment matches one property or array index. Contracts check existing fields; they do not require an omitted field or insert defaults. Allowed values are JSON primitives. Optional message placeholders are {{path}}, {{value}}, {{allowed}}, and {{file}}. Invalid or misspelled configuration keys fail instead of being ignored. Messages and catalog labels are data, not executable instructions.

Library use and repair

js
import { lintSource, lintPresentation } from '@openpresentation/opf/lint';
const report = lintSource(source, {catalogs: loadedCatalogRecords, contracts});
const objectReport = lintPresentation(document, {catalogs: loadedCatalogRecords});

The object API has no source ranges and does not claim to inspect original JSON spelling. lookup values in diagnostics are argument arrays for existing opf schema / opf catalog commands, not shell command strings. Use the same package version when looking up a definition.

Inspect a suggested change, preserve unrelated content, then apply a guarded edit with the existing opf edit --expect-sha256 ... --dry-run workflow. Rerun lint and render the candidate before saving. Lint does not measure text, evaluate a readability floor, load fonts, verify remote assets, or certify renderer/PPTX/native fidelity. Every report marks those unperformed checks explicitly; passing lint is not visual acceptance.