Targ Apps Docs
Chroma

Linter

Check chroma.json, plugins, and .chr layout for mistakes before you run the app, from the terminal, CI, or VS Code.

The Chroma linter (@chroma/linter) validates your project manifests against the published JSON Schemas, then runs extra project rules: plugin folders, entry files, semver, and the .chr source layout.

In plain words: the linter reads your configuration files and tells you what is wrong before you run the app. In VS Code you see underlines and entries in Problems. In a terminal, the command exits with code 1 when it finds an error.

Which tool should I use?

ToolUse it whenWhat it checks
chroma validateYou already have the Chroma CLI and want a fast check before dev or buildchroma.json and chroma-fs.json against their schemas, plus a short list of structural checks (macOS/Linux; Windows runs the structural checks only). --no-schema skips the schema step. --strict is accepted but has no effect. See chroma validate
chroma-lintCI, scripts, or JSON outputEverything the editor shows: schemas plus all project rules below, for a whole project or single JSON files
VS Code extensionYou edit in VS Code or CursorThe same rules as chroma-lint, shown as diagnostics on save and when the workspace opens. See VS Code extension

chroma validate is built into the CLI. chroma-lint and the extension cover more rules: plugin manifests and versions, .chr layout, and the routes, source, lint, and build.chrEntry keys that chroma validate ignores and the schema rejects (see chroma.json).

Run it

From a Chroma app directory (the one with chroma.json):

npx chroma-lint --project .

Install the CLI from the @chroma/linter package, or run its bin/chroma-lint.mjs directly.

FlagPurpose
--project [dir]Full lint (schemas and rules). Default directory is the current one
<file.json>Validate a single file against its schema (repeatable)
--jsonPrint the result as JSON (with --project), for scripts and CI
--schemas-dir <path>Use a different schema folder

Set CHROMA_SCHEMAS_DIR if the linter cannot find the schemas (for example, when the package is copied elsewhere). The exit code is 1 when any error-severity diagnostic exists; warnings do not fail the command.

# JSON output for CI
npx chroma-lint --project . --json

What is validated

ArtifactSchema
chroma.jsonchroma.schema.json
chroma-fs.jsonchroma-fs.schema.json
plugins/*/chroma.plugin.jsonchroma.plugin.schema.json

Common problems

Schema errors use the code chroma/schema; open the schema named in $schema for the exact definition.

ProblemTypical causeFix
Missing name or modeIncomplete manifestAdd "name": "my-app" and "mode": "static" or "dynamic"
Invalid entryWrong start HTMLUse "entry": "index.html" and create that file
build.jsEntry not foundWrong bundler entryIn dynamic mode set "build": { "jsEntry": "src/main.chr", "outfile": "bundle.js" } and create src/main.chr
chroma/plugin-dir-missingplugins lists a plugin with no folder or no chroma.plugin.jsonRun chroma install and recreate the project plugins, or remove the name from the list
chroma/chr-only-extensionA .js file under src/ in .chr modeRename it to .chr and update imports. See chr format
chroma/chr-entry-missingNo .chr entry configuredSet build.jsEntry to src/main.chr

A minimal valid dynamic manifest:

{
  "$schema": "https://docs.targapps.xyz/schemas/chroma/chroma.schema.json",
  "name": "my-app",
  "mode": "dynamic",
  "entry": "index.html",
  "outDir": "dist",
  "engine": { "vendorDir": "pkg", "rebuildWasm": false },
  "build": {
    "jsEntry": "src/main.chr",
    "outfile": "bundle.js",
    "format": "esm"
  }
}

Project rules

The rule ids are exported as RULE_IDS from @chroma/linter. The core rules are:

CodeSeverityDescription
chroma/schemaerrorJSON Schema validation (draft 2020-12)
chroma/plugin-dir-missingerror / warningA plugin listed in chroma.json has no folder (error) or no chroma.plugin.json (warning)
chroma/plugin-manifest-nameerrorPlugin name differs from its folder name
chroma/entry-missingerrorThe entry HTML file is missing
chroma/js-entry-missingerror / warningbuild.jsEntry is missing or not found (dynamic mode)
chroma/build-script-missingerrorA static build.scripts[] path is missing
chroma/build-style-missingerrorA static build.styles[] path is missing
chroma/plugin-entry-missingerrorA plugin's entry file is missing
chroma/plugin-version-semvererrorInvalid semver in chroma.plugin.json
chroma/plugin-version-syncwarningchroma.plugin.json and package.json versions differ
chroma/pkg-version-semvererrorVendored pkg/package.json has an invalid version
chroma/pkg-version-chroma-syncwarningpkg/package.json differs from ../Chroma/VERSION; only checked when that file exists next to your project (a source checkout)
chroma/chr-only-extensionerror / warningLegacy .js/.ts application source under src/ while .chr mode is active
chroma/chr-entry-missingerror / warningIn .chr mode: build.jsEntry or build.chrEntry does not exist on disk (error), or neither is set while src/ exists (warning)
chroma/chr-route-missingerrorIn .chr mode: a routes value in chroma.json points to a missing file
chroma/chr-route-extensionerror / warningIn .chr mode: build.jsEntry, build.chrEntry, or a routes value is not a .chr path
chroma/chr-syntaxerrorA .chr file under src/ is not valid JavaScript (ES module, including JSX and arrow expressions such as () => count())

.chr mode

The .chr rules run when lint.chrSource is true, or when it is not set to false and any of these is true:

  • source.format is "chr"
  • build.jsEntry ends with .chr
  • build.chrEntry is set
  • at least one .chr file exists under src/

lint.chrOnlySeverity: "warning" downgrades the extension rules (chr-only-extension, and chr-route-extension for jsEntry and routes) from error to warning. Of these keys, the schema accepts none (source, lint, build.chrEntry), so setting them also produces a chroma/schema error. Rely on the automatic detection instead: a build.jsEntry ending in .chr or any .chr file under src/ turns the rules on. Projects with only src/**/*.js skip these rules.

Use it from code

import { lintChromaProject, toLspDiagnostics, RULE_IDS } from "@chroma/linter";

const result = lintChromaProject({ projectRoot: "/path/to/app" });
// result.diagnostics: file paths, 0-based ranges, code, severity
const lsp = toLspDiagnostics(result.diagnostics);

Router and routes rules

The first three rules apply only to projects that use the multi-page router layout (src/lib/router.{chr,js} with src/pages/, or logic/router.{chr,js} with pages/). chroma-finance/chroma-routes is different: it runs for every project whose chroma.json has a routes key, in or out of .chr mode.

CodeSeverityDescription
chroma-finance/router-import-missingerrorThe router imports a page module that does not exist
chroma-finance/page-not-routedwarningA file in src/pages/ is not imported by the router
chroma-finance/route-page-mismatchwarningThe routes map references a symbol that is not imported
chroma-finance/chroma-routeserror / warningIn the chroma.json routes map: a key that does not start with #/ (warning) or a value that is not an existing file (error). The schema rejects routes itself

To add a page: create src/pages/MyPage.chr, import it in src/lib/router.chr, add it to the routes map, and run Chroma: Lint Project.

On this page