From 3230bf243822976099a6999b27af872482effdc1 Mon Sep 17 00:00:00 2001 From: Mark Dumay <61946753+markdumay@users.noreply.github.com> Date: Tue, 28 Jul 2026 13:43:38 +0200 Subject: [PATCH] fix: cast whole-number floats to int in argument validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Data that round-trips through JSON delivers every number as float64 — notably page frontmatter injected by CloudCannon's visual editor (Bookshop live rendering). Int-typed arguments such as cols, padding, width, and limit then failed the type check with 'expected type int, got float64' even though the YAML source declares plain integers, breaking live preview for any component with an int argument. Cast float values with no fractional part losslessly to int, mirroring the existing string-to-int casting; fractional values still fail the type check. Adds whole-float and fractional-float golden cases. Co-Authored-By: Claude Fable 5 --- exampleSite/data/tests/casting.yml | 9 +++++++ layouts/_partials/utilities/Args.html | 8 ++++++ tests/golden/casting.json | 39 +++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/exampleSite/data/tests/casting.yml b/exampleSite/data/tests/casting.yml index c80137e..1ff6fc2 100644 --- a/exampleSite/data/tests/casting.yml +++ b/exampleSite/data/tests/casting.yml @@ -51,3 +51,12 @@ cases: structure: test-cast args: items-list: [1, "two", true] + # Whole-number floats cast losslessly to int (JSON round-trips — e.g. frontmatter + # injected by CloudCannon's visual editor — deliver every number as float64). + - name: whole-float-to-int + structure: test-cast + args: {count: 42.0} + # Fractional floats still fail the int type check. + - name: fractional-float-to-int + structure: test-cast + args: {count: 42.5} diff --git a/layouts/_partials/utilities/Args.html b/layouts/_partials/utilities/Args.html index 0220b24..2d65a8b 100644 --- a/layouts/_partials/utilities/Args.html +++ b/layouts/_partials/utilities/Args.html @@ -133,6 +133,14 @@ {{ $value = float $value }} {{ $kind = "float" }} {{ end }} + {{ else if and (eq $kind "float") (in $node.accepts "int") (not (in $node.accepts "float")) (eq (float (int $value)) (float $value)) }} + {{/* whole-number floats cast losslessly to int. Data that round-trips through + JSON — notably page frontmatter injected by CloudCannon's visual editor + (Bookshop live rendering) — arrives with every number as float64, so an + int argument would otherwise reject values the YAML source declares as + plain integers. Fractional values still fail the type check below. */}} + {{ $value = int $value }} + {{ $kind = "int" }} {{ else if and (in $node.accepts "string") (in (slice "bool" "int" "float") $kind) (not (in $node.accepts $kind)) }} {{ $value = string $value }} {{ $kind = "string" }} diff --git a/tests/golden/casting.json b/tests/golden/casting.json index 7a7497d..599ed8e 100644 --- a/tests/golden/casting.json +++ b/tests/golden/casting.json @@ -62,6 +62,27 @@ ] } }, + "fractional-float-to-int": { + "args": { + "args": { + "count": 42.5 + }, + "defaulted": [], + "err": true, + "errmsg": [ + "[test-cast] argument 'count': expected type 'int', got 'float64' with value '42.5'" + ], + "warnmsg": [] + }, + "initargs": { + "default": [], + "err": true, + "errmsg": [ + "[test-cast] argument 'count': expected type 'int', got 'float64' with value '42.5'" + ], + "warnmsg": [] + } + }, "generic-dict-as-map": { "args": { "args": { @@ -233,6 +254,24 @@ "warnmsg": [] } }, + "whole-float-to-int": { + "args": { + "args": { + "count": 42 + }, + "defaulted": [], + "err": false, + "errmsg": [], + "warnmsg": [] + }, + "initargs": { + "count": 42, + "default": [], + "err": false, + "errmsg": [], + "warnmsg": [] + } + }, "wrong-type-errors": { "args": { "args": {