Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | 1x 134x 14070x 86x 16x 2x 294x 2x 86x 2x 16x 16x 2x | // SPDX-License-Identifier: MIT
/**
* YAML emitter for the Home Assistant adapter.
*
* Hand-rolls a deterministic Home Assistant `themes.yaml` document from the Turbo
* theme registry. js-yaml is intentionally not used at runtime so the adapter has
* no serialization dependency; tests parse the output with js-yaml to validate it.
*/
import { flavors } from '@lgtm-hq/turbo-themes-core';
import { mapTokensToHomeAssistant } from './mapping.js';
import { AUTO_THEME_PAIRINGS, assertPairingsValid, resolveAutoTheme } from './pairings.js';
import { renderKey, renderValue } from './yaml.js';
const HEADER = [
'# Generated by @lgtm-hq/turbo-themes/adapters/home-assistant',
'# Do not edit by hand.',
].join('\n');
/**
* Render a variable mapping as indented `key: "value"` lines.
*
* The mapping preserves {@link REQUIRED_KEYS} order (produced by
* {@link mapTokensToHomeAssistant}), so iterating its entries is deterministic.
*
* @param mapping - Variable name → value record.
* @param indent - Leading indent applied to every line.
*/
function renderMapping(mapping: Record<string, string>, indent: string): string[] {
return Object.entries(mapping).map(
([key, value]) => `${indent}${renderKey(key)}: ${renderValue(value)}`,
);
}
/** Emit a single flat theme block (top-level keys only). */
function emitFlatTheme(name: string, mapping: Record<string, string>): string {
return [`${renderKey(name)}:`, ...renderMapping(mapping, ' ')].join('\n');
}
/**
* Emit an auto theme block: the dark mapping at the top level, plus a `modes:`
* block carrying the full dark and light mappings.
*/
function emitAutoTheme(
name: string,
darkMapping: Record<string, string>,
lightMapping: Record<string, string>,
): string {
return [
`${renderKey(name)}:`,
...renderMapping(darkMapping, ' '),
' modes:',
' dark:',
...renderMapping(darkMapping, ' '),
' light:',
...renderMapping(lightMapping, ' '),
].join('\n');
}
/**
* Generate the Home Assistant themes YAML document.
*
* Flat themes are emitted first, sorted alphabetically by theme id for
* determinism, followed by the auto (dark/light paired) themes in
* {@link AUTO_THEME_PAIRINGS} order.
*
* @returns The full YAML document as a string.
* @throws If any auto-theme pairing is invalid (see {@link assertPairingsValid}).
*/
export function generateHomeAssistantThemes(): string {
assertPairingsValid();
const sorted = [...flavors].sort((a, b) => a.id.localeCompare(b.id, 'en'));
const blocks = sorted.map((flavor) =>
emitFlatTheme(flavor.label, mapTokensToHomeAssistant(flavor.tokens)),
);
for (const pairing of AUTO_THEME_PAIRINGS) {
const { name, darkTokens, lightTokens } = resolveAutoTheme(pairing);
blocks.push(
emitAutoTheme(
name,
mapTokensToHomeAssistant(darkTokens),
mapTokensToHomeAssistant(lightTokens),
),
);
}
return `${HEADER}\n${blocks.join('\n')}\n`;
}
|