Skip to content

Conversation

@heliacer
Copy link

@heliacer heliacer commented Oct 3, 2025

The basics

The details

Resolves

Implements JSON Block Definition interface proposed in #9387

Proposed Changes

Introduces a new JsonBlockDefinition in core/interfaces/

  • Added core/interfaces/i_json_block_definition.ts with JsonBlockDefinition and BlockArg definitions
  • Migrated Block.jsonInit, jsonInitFactory, defineBlocksWithJsonArray and createBlockDefinitionsFromJsonArray to use the new interface
  • Migrated interpolate to use the new BlockArg type
  • Updated all built-in block JSON definitions in blocks/ to explicitly set 'output': null to match the default behaviour in the block factory
  • Removed legacy AnyDuringMigration usage for JSON block definitions

Reason for Changes

Makes it easier to write valid JSON Block definitions without having to always rely on the block factory

Test Coverage

I ran the unit test suite and manually loaded the playground with the updated blocks to ensure they still render and connect correctly

Documentation

Not needed

Additional Information

I found another issue while testing (unrelated to this) in the default Playground, when Categories: (typed variables) is selected, the colour category errors out because of an invalid colour_picker definition

@heliacer heliacer requested a review from a team as a code owner October 3, 2025 21:23
@heliacer heliacer requested a review from gonfunko October 3, 2025 21:23
@heliacer heliacer marked this pull request as draft October 3, 2025 21:23
@heliacer heliacer marked this pull request as ready for review October 3, 2025 21:54
@heliacer
Copy link
Author

heliacer commented Nov 3, 2025

@gonfunko bump, will this be considered? 👀

@gonfunko
Copy link
Contributor

gonfunko commented Nov 3, 2025

Yes, sorry for the delay on this! Will try to get you an initial review this week, thank you for the ping on it.

Copy link
Contributor

@gonfunko gonfunko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks promising; added a couple notes. I'd like to run this by my colleagues before merging, but as you may have seen we're transitioning from Google to the Raspberry Pi Foundation so it may be a couple weeks until things are settled.

Comment on lines 41 to 95
interface CommonArg {
name?: string;
}

/** Input Args */
interface InputValueArg extends CommonArg {
type: 'input_value';
check?: string | string[];
align?: FieldsAlign
}
interface InputStatementArg extends CommonArg {
type: 'input_statement';
check?: string | string[];
}
interface InputDummyArg extends CommonArg {
type: 'input_dummy';
}

/** Field Args */
interface FieldInputArg extends CommonArg{
type: 'field_input'
text: string
}

interface FieldNumberArg extends CommonArg {
type: 'field_number';
value?: number;
min?: number;
max?: number;
precision?: number;
}

interface FieldDropdownArg extends CommonArg {
type: 'field_dropdown';
options: [string, string][];
}

interface FieldCheckboxArg extends CommonArg {
type: 'field_checkbox';
checked?: boolean | 'TRUE' | 'FALSE';
}

interface FieldImageArg {
type: 'field_image';
src: string;
width: number;
height: number;
alt?: string;
flipRtl?: boolean | 'TRUE' | 'FALSE';
}

interface FieldVariableArg extends CommonArg {
type: 'field_variable'
variable: string | null
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each of the Field classes defines and exports a FieldXYZFromJsonConfig interface, which I think should be used in place of these to avoid duplication/skew.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohh, didn't know these configs existed 👀

interface FieldInputArg extends FieldTextInputFromJsonConfig {
  name: string
  type: 'field_input'
}

interface FieldNumberArg extends FieldNumberFromJsonConfig {
  name: string
  type: 'field_number'
}
// ...

wdyt 🤔

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gonfunko bump again, hope this finds you well since there was all the migration from google to rpf and stuff 👀
I would definitely want to include your suggested changes, but before I do, can you run this and lmk if it works?
Thanks 😃

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the bump, and yes, we're all moved over and all is well. The approach to the interfaces you proposed in your prior comment makes sense to me, so updating it along those lines sounds good.

@heliacer heliacer marked this pull request as draft November 8, 2025 17:33
@heliacer
Copy link
Author

heliacer commented Nov 8, 2025

converted this to draft, will implement @gonfunko's review feedback sooner or later :>

@heliacer heliacer marked this pull request as ready for review December 17, 2025 14:23
@heliacer heliacer requested a review from a team as a code owner December 17, 2025 14:23
@heliacer
Copy link
Author

The following new changes have been made:

  • implemented @gonfunko 's idea to use the existing FieldXYZFromJsonConfig interfaces
  • removed unnecessary output: null which have been added by mistake

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new JsonBlockDefinition TypeScript interface to provide type safety when defining blocks using JSON, replacing the previous AnyDuringMigration type. The interface defines the structure for JSON block definitions including properties for styling, connections, messages, arguments, and extensions. However, the PR contains several critical bugs related to the removal of 'output': null declarations from getter blocks, which will break their functionality.

Key Changes

  • Added JsonBlockDefinition and BlockArg interfaces in a new file core/interfaces/i_json_block_definition.ts
  • Migrated jsonInit, jsonInitFactory, and related functions to use the new typed interfaces instead of AnyDuringMigration
  • Updated property access from bracket notation to dot notation throughout block initialization code
  • Removed explicit 'output': null and 'variable': null declarations from block definitions in the blocks/ directory

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
core/interfaces/i_json_block_definition.ts New interface file defining JsonBlockDefinition and BlockArg types with field and input configuration interfaces
core/common.ts Updated function signatures to use JsonBlockDefinition instead of AnyDuringMigration and changed bracket to dot notation for property access
core/block.ts Updated jsonInit and helper methods to use typed interfaces and dot notation; removed backwards compatibility code for hat styles
blocks/variables.ts Removed 'output': null declaration from variables_get block (critical bug - will break block functionality)
blocks/variables_dynamic.ts Removed 'output': null declaration from variables_get_dynamic block (critical bug - will break block functionality)
blocks/loops.ts Removed 'variable': null declarations from loop variable fields (acceptable change - makes fields truly optional)
blocks/logic.ts Removed 'output': null declarations from logic_null and logic_ternary blocks (critical bugs - will break block functionality)
Comments suppressed due to low confidence (1)

core/block.ts:1833

  • The logic in this function is flawed. The condition checks if json.colour is truthy, but then inside that block it checks if json.colour === undefined. If json.colour is truthy (line 1822), it cannot be undefined (line 1823), making the warning on line 1824 unreachable. The outer check should be 'colour' in json instead of json.colour to properly distinguish between missing and undefined properties.
    if (json.colour) {
      if (json.colour === undefined) {
        console.warn(warningPrefix + 'Undefined colour value.');
      } else {
        const rawValue = json.colour;
        try {
          this.setColour(rawValue);
        } catch {
          console.warn(warningPrefix + 'Illegal colour value: ', rawValue);
        }
      }
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@heliacer heliacer requested a review from Copilot December 17, 2025 14:54
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.

Comments suppressed due to low confidence (1)

blocks/variables_dynamic.ts:44

  • Missing 'output': null property for getter block. The variables_get_dynamic block is a getter that should have an output connection to return a value. The removal of this property is incorrect - getter blocks need an output to be used in expressions. Compare with the variables_get block in variables.ts (line 40) which correctly has 'output': null.
  // Block for variable getter.
  {
    'type': 'variables_get_dynamic',
    'message0': '%1',
    'args0': [
      {
        'type': 'field_variable',
        'name': 'VAR',
        'variable': '%{BKY_VARIABLES_DEFAULT_NAME}',
      },
    ],
    'style': 'variable_dynamic_blocks',
    'helpUrl': '%{BKY_VARIABLES_GET_HELPURL}',
    'tooltip': '%{BKY_VARIABLES_GET_TOOLTIP}',
    'extensions': ['contextMenu_variableDynamicSetterGetter'],

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

blocks/logic.ts Outdated

// Register provided blocks.
defineBlocks(blocks);
defineBlocks(blocks)
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent code style across block definition files. This file has been reformatted to remove semicolons and use different import/object spacing, but other files in the blocks/ directory (e.g., blocks/lists.ts, blocks/math.ts, blocks/procedures.ts) still use semicolons and the original formatting. This creates inconsistency across the codebase. Either all block files should be reformatted consistently, or this file should maintain the original style to match the rest of the directory.

Suggested change
defineBlocks(blocks)
defineBlocks(blocks);

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Contributor

@gonfunko gonfunko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! If you could run npm run format and commit the results that should take care of aligning the formatting with our standards and reduce the diff size substantially.

import {FieldVariableFromJsonConfig} from '../field_variable';

export interface JsonBlockDefinition {
type?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type should be required; we can't do anything with a block definition that doesn't specify what type of block it's defining.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, but there are snippets like these which would no longer be accepted, and would require an additional type even though it is is given by assigning blocks itself:

blocks['text_reverse'] = {
  /**
   * Block for reversing a string.
   */
  init: function (this: Block) {
    this.jsonInit({
      'message0': Msg['TEXT_REVERSE_MESSAGE0'],
      'args0': [
        {
          'type': 'input_value',
          'name': 'TEXT',
          'check': 'String',
        },
      ],
      'output': 'String',
      'inputsInline': true,
      'style': 'text_blocks',
      'tooltip': Msg['TEXT_REVERSE_TOOLTIP'],
      'helpUrl': Msg['TEXT_REVERSE_HELPURL'],
    });
  },
};

@BenHenning
Copy link
Collaborator

Since @gonfunko is already looking at this I don't think I need to. Seems like the PR assigner rotation thing kinda goofed during the transition, but not a big deal.

@BenHenning BenHenning removed their request for review December 17, 2025 20:28
@BenHenning BenHenning removed their assignment Dec 17, 2025
Copy link
Contributor

@maribethb maribethb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the pull request! It's great to make some progress in this area. I think there's more work before this is ready to go in, however.

  1. This is a promising start for typing the built-in block definitions, but we need developers to be able to customize it more. For example, if a developer has defined a custom input or field, currently with this typing there is no way for them to provide a valid typed json object because BlockArg type is not going to include their custom object. We need a solution for this.
  2. Because of (1) I would like to see a test case in tests/typescript that registers a custom block using json that uses the custom field already in that test suite (FieldMitosis)
  3. This is going to be a breaking change for typescript developers if jsonInit and related methods require their arguments to conform to this type. I think the best path forward would be to include this interface now but leave the arguments to the various init/define functions as AnyDuringMigration, so that developers can go ahead and update their code to use the new type when they want without being required to do so. In the next major release we could then make the breaking change of requiring the types. This would also let us soft-launch the types and test/change them before we require their use.
  4. You've changed from bracket access to dot access in some of the block initialization code, but I think you should revert those changes. The reason for the bracket access is because Blockly is compatible with closure compiler's advanced compilation mode, which does a lot of property renames. The bracket access says not to rename those properties, which is important if your block definitions are not also compiled with closure compiler. Overall I think block definitions are likely to be compiled, unlike, say, the json from a serialized workspace, where it's important we continue to use bracket access. And, I would guess that approximately nobody except blockly-games (which does not even use this version of blockly) is using advanced compilation anyway. At some point we may decide that we don't care about maintaining that compatibility. But for now, I think there is some risk in removing this (however small) and not much upside. So I would recommend that you revert those changes. They are not really relevant to this change anyway.

@heliacer
Copy link
Author

tests for the custom input / fields will be added soon :D

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.

4 participants