Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions Sources/ExFigCLI/ExFig.docc/Usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@ Validate your Figma file structure against your PKL config before exporting:
# Lint with default rules
exfig lint -i exfig.pkl

# Add lint-only policies from a separate config
exfig lint -i exfig.pkl --lint-config lint.pkl

# Only check specific rules
exfig lint -i exfig.pkl --rules naming-convention,deleted-variables

Expand All @@ -307,17 +310,18 @@ exfig lint -i exfig.pkl --format json --severity error

### Available Rules

| Rule | Severity | Description |
| -------------------------- | -------- | ------------------------------------------------------ |
| `frame-page-match` | error | Frame/page names in config exist in Figma file |
| `naming-convention` | error | Component names match `nameValidateRegexp` patterns |
| `component-not-frame` | error | Configured frames contain published components |
| `duplicate-component-names`| error | No duplicate component names in configured frames |
| `deleted-variables` | warning | No `deletedButReferenced` variables in collections |
| `alias-chain-integrity` | warning | Variable alias chains resolve without broken refs |
| `dark-mode-variables` | error | With `variablesDarkMode`, fills bound to Variables |
| `dark-mode-suffix` | warning | With `suffixDarkMode`, light components have dark pair |
| `path-data-length` | error | Icon SVG pathData within 32,767-byte AAPT limit |
| Rule | Severity | Description |
| --------------------------- | -------- | ------------------------------------------------------ |
| `frame-page-match` | error | Frame/page names in config exist in Figma file |
| `naming-convention` | error | Component names match `nameValidateRegexp` patterns |
| `component-not-frame` | error | Configured frames contain published components |
| `duplicate-component-names` | error | No duplicate component names in configured frames |
| `deleted-variables` | warning | No `deletedButReferenced` variables in collections |
| `alias-chain-integrity` | warning | Variable alias chains resolve without broken refs |
| `dark-mode-variables` | error | With `variablesDarkMode`, fills bound to Variables |
| `dark-mode-suffix` | warning | With `suffixDarkMode`, light components have dark pair |
| `path-data-length` | error | Icon SVG pathData within 32,767-byte AAPT limit |
| `icon-color-variables` | error | Icon paints use configured Figma Variables |

## Help and Version

Expand Down
29 changes: 29 additions & 0 deletions Sources/ExFigCLI/Lint/LintConfigLoader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import ExFigConfig
import Foundation

/// Loads optional lint-only overlay configuration for `exfig lint`.
enum LintConfigLoader {
static let defaultFileName = "lint.pkl"

static func resolvePath(explicitPath: String?, mainConfigPath: String?) -> URL? {
if let explicitPath, !explicitPath.isEmpty {
return URL(fileURLWithPath: explicitPath)
}

let mainPath = mainConfigPath ?? ExFigOptions.defaultConfigFilename
let mainURL = URL(fileURLWithPath: mainPath)
let lintURL = mainURL.deletingLastPathComponent().appendingPathComponent(defaultFileName)

guard FileManager.default.fileExists(atPath: lintURL.path) else {
return nil
}
return lintURL
}

static func load(explicitPath: String?, mainConfigPath: String?) async throws -> ExFigConfig.Lint.ModuleImpl? {
guard let path = resolvePath(explicitPath: explicitPath, mainConfigPath: mainConfigPath) else {
return nil
}
return try await PKLEvaluator.evaluateLintConfig(configPath: path)
}
}
1 change: 1 addition & 0 deletions Sources/ExFigCLI/Lint/LintEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,6 @@ extension LintEngine {
DarkModeSuffixRule(),
PathDataLengthRule(),
InvalidRTLVariantValueRule(),
IconColorVariablesLintRule(),
])
}
2 changes: 2 additions & 0 deletions Sources/ExFigCLI/Lint/LintTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ actor LintDataCache {
struct LintContext {
/// The resolved PKL configuration.
let config: ExFig.ModuleImpl
/// Optional lint-only overlay configuration.
let lintConfig: ExFigConfig.Lint.ModuleImpl?
/// Figma API client.
let client: any FigmaAPI.Client
/// Shared cache for Figma API responses across rules.
Expand Down
Loading
Loading