Skip to content

Fix saving of Blockly scripts: named timers/schedules under Blockly 13, and functions without statements - #2352

Open
krobipd wants to merge 4 commits into
ioBroker:masterfrom
krobipd:fix-blockly-save
Open

Fix saving of Blockly scripts: named timers/schedules under Blockly 13, and functions without statements#2352
krobipd wants to merge 4 commits into
ioBroker:masterfrom
krobipd:fix-blockly-save

Conversation

@krobipd

@krobipd krobipd commented Aug 18, 2026

Copy link
Copy Markdown

What users see

Since 10.1.0, editing an existing Blockly script that contains a named timeout, interval or schedule does not offer the Save button anymore — changes cannot be saved, and edits made after touching such a block are lost (#2349). Independently, since at least 9.0.11 a script containing a function with a return value whose "statements" checkbox is off cannot be saved either (#1958). Because named timers are among the most common blocks, the first one hits a large share of all existing Blockly scripts.

Why it happens

The editor decides "something changed — show Save" only after it has regenerated the script: workspaceToCode plus Xml.workspaceToDom (the workspace XML travels as a base64 comment behind the generated code). When any single block throws inside that chain, the change handler dies and the Save button never appears — without any visible error.

Two blocks throw:

  • The timer and schedule blocks answer getVarModels() with hand-built variable models in the Blockly 11 shape ({ getId, name, type }). Blockly 13 reads variable models through methods — Xml.variablesToDom calls getName()/getType() while saving — so every save of a script containing timeouts_settimeout, timeouts_settimeout_variable, timeouts_setinterval, timeouts_setinterval_variable or schedule_create throws getName is not a function.
  • The function generator reads the STACK input unconditionally. With "statements" unchecked the mutation removes that input, and statementToCode throws for a missing one — the same trap the RETURN input is already guarded against one line below.

The fix

  • The pseudo variable models now carry getName()/getType()/getId(); the bare name/type properties stay for adapter block files written against the Blockly 11 shape (the compatibility promise of BLOCKLY_TS.md).
  • The STACK read is guarded exactly like the RETURN read below it.

Why the snapshots stayed green

The snapshot harness only exercises the code generation, and that path touches getId() only. The harness now additionally saves every corpus block the way the editor does it and reloads the saved XML to the same code — with the two fixes reverted, this new test fails on exactly the five affected block types. A new fixture covers the statement-less function, a state the toolbox (statements on by default) can never show.

Verified

  • npm run test:blockly — 25 passing; with the fixes reverted: 3 failing (the new save test, the new fixture, the snapshot diff)
  • ✅ Snapshot diff is empty except for the new fixture's entry — no existing block's generated code changed
  • npm run lint, npm run test:package, npm run build, npm test (declarations + integration, 280 passing) locally on Node 22

Fixes #2349
Fixes #1958

🤖 Generated with Claude Code

krobipd and others added 4 commits August 18, 2026 22:25
The timer and schedule blocks answer getVarModels() with hand-built
variable models in the Blockly 11 shape ({ getId, name, type }).
Blockly 13 reads variable models through methods - Xml.variablesToDom
calls getName()/getType() while the workspace is serialized for saving -
so every save of a script containing such a block threw
'getName is not a function'. The editor's change handler died on that,
and with it the save button (ioBroker#2349).

The models now carry the methods; the bare properties stay for adapter
block files written against the Blockly 11 shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With 'statements' unchecked in the function's mutator dialog the
mutation removes the STACK input, and statementToCode() throws for a
missing input - the same trap the RETURN input is already guarded
against one line below. The throw suppressed the save button for every
script containing such a function (ioBroker#1958).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The snapshots only exercise workspaceToCode, but the editor saves with
workspaceToCode PLUS Xml.workspaceToDom - the workspace XML travels as
a base64 comment behind the generated code. A block whose serialization
throws suppresses the save button without any snapshot moving, which is
exactly how the timer blocks' variable models broke under Blockly 13.

Every corpus entry is now saved the way the editor does it and the
saved XML reloaded to the same code. A new fixture covers the function
with a return value and no statements, whose generator branch the
toolbox (statements on by default) could never reach.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Blockly changes cannot be saved in 10.1.0 [Bug]: No "Save" button when there is a function with a result without "allow operators" checkbox

1 participant