Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
22 changes: 18 additions & 4 deletions packages/appkit/src/type-generator/mv-registry/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,17 @@ function compareKeys(a: string, b: string): number {
* Read {@link METRIC_CONFIG_FILE} from a metric-views folder
* (`config/metric-views/`).
*
* Returns `null` if the file does not exist (the metric-view path is
* additive — apps without definitions.json must not be penalized). There is
* deliberately no fallback to a legacy filename.
* Returns `null` if the file does not exist, is empty, or has not been rendered
* yet (the metric-view path is additive — apps without a defined
* definitions.json must not be penalized). The template ships definitions.json
* wrapped in a `{{if .plugins.analytics}}` guard, so scaffolding without the
* analytics plugin leaves the file empty, and running typegen against the raw
* (un-rendered) template leaves the literal `{{...}}` in place — neither is a
* misconfiguration, so both are treated the same as an absent file rather than
* a fatal parse error. There is deliberately no fallback to a legacy filename.
*
* Throws on JSON parse errors so misconfiguration surfaces loudly.
* Throws on JSON parse errors in a *non-empty, rendered* file so genuine
* misconfiguration still surfaces loudly.
*/
export async function readMetricConfig(
metricViewsFolder: string,
Expand All @@ -66,6 +72,14 @@ export async function readMetricConfig(
throw err;
}

// An empty file (analytics plugin off → the `{{if}}` guard renders nothing)
// or an un-rendered template (raw `{{...}}` left in place during typegen on
// the template itself) means "no metric views" — treat like an absent file.
const trimmed = raw.trim();
if (trimmed === "" || trimmed.startsWith("{{")) {
return null;
}

let parsed: unknown;
try {
parsed = JSON.parse(raw);
Expand Down
23 changes: 23 additions & 0 deletions packages/appkit/src/type-generator/tests/mv-registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,29 @@ describe("readMetricConfig", () => {
expect(await readMetricConfig(tmpDir)).toBeNull();
});

test("returns null for an empty definitions.json (analytics plugin off)", async () => {
// The template wraps definitions.json in `{{if .plugins.analytics}}`, so
// scaffolding without the analytics plugin renders an empty file. That's
// "no metric views", not a parse error.
await fs.writeFile(path.join(tmpDir, "definitions.json"), "");
expect(await readMetricConfig(tmpDir)).toBeNull();
});

test("returns null for a whitespace-only definitions.json", async () => {
await fs.writeFile(path.join(tmpDir, "definitions.json"), "\n \n");
expect(await readMetricConfig(tmpDir)).toBeNull();
});

test("returns null for an un-rendered template definitions.json", async () => {
// Running typegen against the raw template (e.g. npm-install warmup before
// `appkit setup`) leaves the Go-template guard literal in place.
await fs.writeFile(
path.join(tmpDir, "definitions.json"),
'{{if .plugins.analytics -}}\n{ "metricViews": {} }\n{{- end}}\n',
);
expect(await readMetricConfig(tmpDir)).toBeNull();
});

test("parses a valid definitions.json", async () => {
await fs.writeFile(
path.join(tmpDir, "definitions.json"),
Expand Down
Loading