Skip to main content

Tutorial

This tutorial walks you through your first ignition-lint run, from a fresh install to fixing your first violations. It assumes no prior knowledge of the framework — it's the equivalent of skipping the manual and jumping in.

By the end you will have:

  • Installed ignition-lint
  • Run it on a sample Perspective view
  • Read and understood the violation output
  • Looked up a rule in the docs
  • Configured a rule and re-run

Install

If you haven't already, install ignition-lint from PyPI:

pip install ign-lint

Verify it's available:

ign-lint --help

You should see a usage banner listing the supported arguments. The ones we'll use in this tutorial are:

ArgumentPurpose
<file> (positional)Lint a single view.json
--files <pattern>Lint every file matching a glob
--config <path>Use a rule_config.json
--verbosePrint per-rule timing and discovery info

For a full reference of the CLI, see the command line guide.

Get a sample view

Save the following as dashboard.json. It's a deliberately small Perspective view with a couple of intentional problems — perfect for the tutorial.

{
"root": {
"type": "ia.container.flex",
"meta": { "name": "root" },
"props": {},
"children": [
{
"type": "ia.input.button",
"meta": { "name": "my_button" },
"props": { "text": "Refresh" },
"events": {
"component": {
"onActionPerformed": {
"type": "script",
"script": "self.getSibling('my_button').props.text = 'clicked'",
"scope": "G"
}
}
}
},
{
"type": "ia.display.label",
"meta": { "name": "TimeLabel" },
"propConfig": {
"props.text": {
"binding": {
"type": "expr",
"config": { "expression": "now(5000)" }
}
}
}
}
]
},
"custom": {
"unusedSetting": "neverReferenced"
}
}

The view has:

  • A button named my_button (lowercase + underscore — not PascalCase)
  • A script using self.getSibling() (a brittle traversal pattern)
  • A label binding now(5000) (5-second polling, below the default 10-second minimum)
  • A custom property unusedSetting that is never referenced

That's four problems across four different rules. Let's see ignition-lint find them.

Run ign-lint

In the same directory as dashboard.json, run:

ign-lint dashboard.json

With no --config flag, every registered rule runs with its default settings. You should see output similar to:

Found 5 issues in dashboard.json:

NamePatternRule (warning):
• root.children[0].meta.name: Name 'my_button' doesn't follow PascalCase for component (suggestion: 'MyButton')

BadComponentReferenceRule (error):
• root.children[0].events.component.onActionPerformed: Script contains '.getSibling(' which creates brittle view structure dependencies. Consider using view.custom properties or message handling for component communication instead.

ComponentReferenceValidationRule (error):
• root.children[0].events.component.onActionPerformed: Script references non-existent sibling component 'my_button'

PollingIntervalRule (error):
• root.children[1].propConfig.props.text.binding: 'now(5000)'

UnusedCustomPropertiesRule (error):
• custom.unusedSetting: custom property 'unusedSetting' is defined but never referenced

Summary: 4 errors, 1 warning

ign-lint exits with code 1 because there are error-severity violations. Warnings on their own would still exit 0 unless you enable strict-warning mode.

Anatomy of a violation

Every violation line follows the same shape:

<JSON path>: <human-readable message>

Pick the first violation:

root.children[0].meta.name: Name 'my_button' doesn't follow PascalCase for component (suggestion: 'MyButton')

This tells you four things:

PartValue
Pathroot.children[0].meta.name
Rule that firedNamePatternRule (from the section header above the bullet)
Severitywarning (also in the section header)
What to doRename to MyButton (the suggestion in parentheses)

The path uses bracket notation for array indices and dot notation for object keys, exactly as it appears when the framework flattens the view JSON.

Look up a rule

When you see a rule name you don't recognize, the docs have a dedicated page for each one. The structure is:

For example, NamePatternRule is documented at Rules → Naming → NamePatternRule. Open it now and skim. You'll see the basic config, common configurations, and a link to the full reference for every option.

The user-guide page covers the 80% case. The reference page covers everything.

Fix one violation

Edit dashboard.json and rename the button:

- "meta": { "name": "my_button" },
+ "meta": { "name": "RefreshButton" },

Also update the script that referenced it (this would otherwise still target my_button):

- "script": "self.getSibling('my_button').props.text = 'clicked'",
+ "script": "self.getSibling('RefreshButton').props.text = 'clicked'",

Run again:

ign-lint dashboard.json
Found 4 issues in dashboard.json:

BadComponentReferenceRule (error):
• root.children[0].events.component.onActionPerformed: Script contains '.getSibling(' …

ComponentReferenceValidationRule (error):
• root.children[0].events.component.onActionPerformed: Script references non-existent sibling component 'RefreshButton'

PollingIntervalRule (error):
• root.children[1].propConfig.props.text.binding: 'now(5000)'

UnusedCustomPropertiesRule (error):
• custom.unusedSetting: custom property 'unusedSetting' is defined but never referenced

The naming warning is gone. The structure violations remain because we didn't add a sibling component named RefreshButton — the script still references something that doesn't exist. That's ComponentReferenceValidationRule working: it caught a real bug. (We won't fix that one in this tutorial — just notice that it's there.)

Configure a rule

Suppose your team uses camelCase for component names instead of PascalCase. You configure ignition-lint with a rule_config.json file. Save the following alongside dashboard.json:

{
"NamePatternRule": {
"kwargs": {
"convention": "camelCase"
}
}
}

Now rerun, pointing at the config:

ign-lint --config rule_config.json dashboard.json

The naming rule now expects camelCase. Other rules continue to run with their defaults — they will be activated even though they aren't listed in the config, because the config is an override layer rather than an allowlist.

To disable a rule entirely, add "enabled": false:

{
"PollingIntervalRule": {
"enabled": false
}
}

For the full configuration schema, see the configuration guide.

Lint many files at once

Real projects have many views. The --files flag accepts a glob pattern:

ign-lint --config rule_config.json --files "views/**/view.json"

Quote the pattern. ignition-lint expands the glob internally rather than relying on the shell, which keeps very large projects from hitting ARG_MAX limits.

Where to go next

You've now run ignition-lint, read its output, looked up a rule, and configured one. From here:

  • Adopting on an existing repo? Read the whitelist guide — it lets you exempt legacy files while still gating new code.
  • Want this in CI? See GitHub Actions.
  • Want pre-commit gating? See pre-commit.
  • Want to write a custom rule? Start with Architecture, then Creating rules.
  • Want to see every built-in rule? Open the Rules section in the sidebar.