From 864d31aa65c73dffb3500b83e521fc9bf2b234b8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 17 Apr 2026 14:53:59 -0500 Subject: [PATCH] [core] Put ComponentMarker outside the IIFE as a visual bracket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The marker comment was being emitted as the first line *inside* each IIFE: []() { // === logger === // logger: // ... ... }(); That works but buries the component label inside the lambda body, so scanning generated main.cpp to find "where does component X's setup live" is harder than it needs to be. Emit the marker before and after the IIFE instead: // === logger === []() { // logger: // ... ... }(); // === logger === Comment-only components (e.g. sha256, async_tcp, empty platforms like binary_sensor:) don't grow a useless trailing duplicate marker — when there's no IIFE to bracket, the marker is emitted once. --- esphome/core/__init__.py | 27 ++++++++++++++++++--------- tests/unit_tests/test_core.py | 19 +++++++++++++++++-- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index b66899e1338..acf2e197ac1 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -1052,13 +1052,16 @@ class EsphomeCore: # Split main_statements at ComponentMarker sentinels into a prefix # (statements emitted before any component) plus per-component groups. + # Each group's first entry is its marker comment, kept separate so + # it can bracket the IIFE rather than be buried inside it. prefix: list[str] = [] - components: list[list[str]] = [] + components: list[tuple[str, list[str]]] = [] current = prefix for exp in self.main_statements: if isinstance(exp, ComponentMarker): - current = [str(exp).rstrip()] - components.append(current) + body: list[str] = [] + components.append((str(exp).rstrip(), body)) + current = body continue current.append(str(statement(exp)).rstrip()) @@ -1066,13 +1069,19 @@ class EsphomeCore: if not components: return "\n".join(prefix) + "\n\n" - # Each component's block is wrapped in a noinline IIFE lambda so its - # stack frame is released on return, bounding peak stack during - # setup(). Large blocks are sub-split to cap single heavy components - # (e.g. sensor platforms with many filter registrations). + # Each component's block is wrapped in IIFE lambdas so its stack + # frame is released on return, bounding peak stack during setup(). + # Large blocks are sub-split to cap single heavy components (e.g. + # sensor platforms with many filter registrations). The marker + # comment brackets the IIFE on both sides so the generated + # main.cpp is easy to scan by component. pieces = list(prefix) - for block in components: - pieces.extend(_wrap_in_iifes(block, max_statements=50)) + for marker, body in components: + wrapped = _wrap_in_iifes(body, max_statements=50) + pieces.append(marker) + pieces.extend(wrapped) + if any("[]()" in line for line in wrapped): + pieces.append(marker) return "\n".join(pieces) + "\n\n" @property diff --git a/tests/unit_tests/test_core.py b/tests/unit_tests/test_core.py index cd65e670ed7..dc16d4cfe9c 100644 --- a/tests/unit_tests/test_core.py +++ b/tests/unit_tests/test_core.py @@ -961,8 +961,23 @@ def test_cpp_main_section_component_marker_wraps_in_iife() -> None: out = target.cpp_main_section assert out.count("[]() {") == 2 assert out.count("}();") == 2 - assert "// === logger ===" in out - assert "// === wifi ===" in out + # Each component's marker brackets its IIFE (once before, once after). + assert out.count("// === logger ===") == 2 + assert out.count("// === wifi ===") == 2 + + +def test_cpp_main_section_comment_only_component_emits_single_marker() -> None: + # A component that emits no C++ statements (only a ComponentMarker) + # should not grow a useless trailing duplicate marker. + target = core.EsphomeCore() + target.main_statements = [ + ComponentMarker("sha256"), + ComponentMarker("wifi"), + RawStatement("new_wifi();"), + ] + out = target.cpp_main_section + assert out.count("// === sha256 ===") == 1 + assert out.count("// === wifi ===") == 2 # wifi has an IIFE def test_cpp_main_section_prefix_statements_stay_outside_iife() -> None: