This document describes the guarantees around reproducible, deterministic outputs from the module processing pipeline.
Deterministic outputs provide several benefits:
All JSON files generated by the pipeline (modules.json, modules.min.json, stats.json, metadata files) have the following guarantees:
Implementation: The stringifyDeterministic() function in scripts/shared/deterministic-output.ts recursively sorts all object keys before serialization.
Example:
{
"description": "A weather module",
"id": "MMM-Weather",
"maintainer": "example",
"url": "https://github.com/example/MMM-Weather"
}
Module screenshots are stored with deterministic filenames to ensure:
Implementation: The createDeterministicImageName() function uses the module identifier (moduleName---maintainer) directly as the filename base.
Format: <moduleName>---<maintainer>.<extension>
Example:
MMM-Weather by exampleMMM-Weather---example.jpg (always the same for this module)Previous approach used original source filenames, which caused issues:
❌ Old: MMM-Weather---example---path/to/screenshot.jpg
✅ New: MMM-Weather---example.jpg
Problems with old approach:
Benefits of simple deterministic approach:
import { writeJson } from "./shared/fs-utils.ts";
// Automatically uses sorted keys
await writeJson("output.json", { b: 2, a: 1, c: 3 });
// Result: {"a": 1, "b": 2, "c": 3}
import { stringifyDeterministic } from "./shared/deterministic-output.ts";
const data = { z: 26, a: 1, m: 13 };
const json = stringifyDeterministic(data, 2);
// Result: "{\n \"a\": 1,\n \"m\": 13,\n \"z\": 26\n}"
import { createDeterministicImageName } from "./shared/deterministic-output.ts";
const filename = createDeterministicImageName("MMM-Weather", "example", "jpg");
// Result: "MMM-Weather---example.jpg" (deterministic, always the same)
To verify deterministic output:
# Run pipeline twice
node --run pipeline
# Copy output
cp website/data/modules.json /tmp/modules-run1.json
# Run pipeline again
node --run pipeline
# Compare outputs - should be identical
diff website/data/modules.json /tmp/modules-run1.json
No diff means perfect reproducibility.
The sortObjectKeys() function recursively processes values:
null, string, number, boolean): returned as-isThis ensures deterministic output at all nesting levels.
Screenshot filenames follow a simple, deterministic pattern:
${moduleName}---${maintainer}.${extension}MMM-Weather---example.jpgNo hashing required - the module identifier itself is already unique and deterministic.
Some older snapshots in the repository may still contain pre-standardized screenshot filenames (for example, names derived from source paths). The current canonical output uses the deterministic <moduleName>---<maintainer>.<extension> format.
Downstream consumers should always read screenshot paths from modules.json rather than hard-coding file names.
Deterministic output safeguards (sorted keys and deterministic image naming) are part of the current pipeline behavior.