From 2753bb4a4362d7c8cce8f452320ee5e2f5dee0c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Nov 2025 03:46:40 +0000 Subject: [PATCH 1/3] Initial plan From e67ab4d0b1fda0a1fea9001865a32007d470d4d2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Nov 2025 03:52:08 +0000 Subject: [PATCH 2/3] Add @extends annotation support for single-file components Co-authored-by: grantcopley <1197835+grantcopley@users.noreply.github.com> --- models/EmptySingleFileComponent.bx | 2 +- models/EmptySingleFileComponent.cfc | 2 +- models/SingleFileComponentBuilder.cfc | 20 ++++++++++++++++++- test-harness/tests/specs/CBWIRESpec.cfc | 10 ++++++++++ ...ire_from_single_file_boxlang_component.bxm | 12 +++++++++++ ...om_basewire_from_single_file_component.cfm | 12 +++++++++++ 6 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 test-harness/wires/test/should_extend_custom_basewire_from_single_file_boxlang_component.bxm create mode 100644 test-harness/wires/test/should_extend_custom_basewire_from_single_file_component.cfm diff --git a/models/EmptySingleFileComponent.bx b/models/EmptySingleFileComponent.bx index ac22dc0c..676d96ab 100644 --- a/models/EmptySingleFileComponent.bx +++ b/models/EmptySingleFileComponent.bx @@ -1,4 +1,4 @@ -class extends="cbwire.models.Component" { +class extends="{{ EXTENDS_PATH }}" { {{ CFC_CONTENTS }} diff --git a/models/EmptySingleFileComponent.cfc b/models/EmptySingleFileComponent.cfc index 4f95e6d0..a6877a69 100644 --- a/models/EmptySingleFileComponent.cfc +++ b/models/EmptySingleFileComponent.cfc @@ -1,4 +1,4 @@ -component extends="cbwire.models.Component" { +component extends="{{ EXTENDS_PATH }}" { {{ CFC_CONTENTS }} diff --git a/models/SingleFileComponentBuilder.cfc b/models/SingleFileComponentBuilder.cfc index b4c1e983..b2f1dbeb 100644 --- a/models/SingleFileComponentBuilder.cfc +++ b/models/SingleFileComponentBuilder.cfc @@ -46,6 +46,7 @@ component accessors="true" singleton { local.fileContents = fileRead( arguments.cfmPath ); local.singleFileContents = ""; local.remainingContents = ""; + local.extendsPath = "cbwire.models.Component"; local.startedWire = false; local.endedWire = false; @@ -64,6 +65,15 @@ component accessors="true" singleton { local.insideScriptTag = false; } + // Parse @extends annotation + if ( local.insideScriptTag && local.line contains "@extends" ) { + local.extendsMatch = reFindNoCase( "@extends\s*\(\s*['""]?([^'"")\s]+)['""]?\s*\)", local.line, 1, true ); + if ( arrayLen( local.extendsMatch.match ) >= 2 && len( local.extendsMatch.match[ 2 ] ) ) { + local.extendsPath = local.extendsMatch.match[ 2 ]; + } + continue; + } + if ( local.insideScriptTag && local.line contains "@startWire" ) { local.startedWire = true; continue; @@ -82,7 +92,8 @@ component accessors="true" singleton { return { "singleFileContents" : local.singleFileContents, - "remainingContents" : local.remainingContents + "remainingContents" : local.remainingContents, + "extendsPath" : local.extendsPath }; } @@ -151,6 +162,13 @@ component accessors="true" singleton { "one" ); + local.emptySingleFileComponent = replaceNoCase( + local.emptySingleFileComponent, + "{{ EXTENDS_PATH }}", + local.parsedContents.extendsPath, + "one" + ); + local.uuid = createUUID(); fileWrite( local.tmpClassPath, local.emptySingleFileComponent ); diff --git a/test-harness/tests/specs/CBWIRESpec.cfc b/test-harness/tests/specs/CBWIRESpec.cfc index b21a2916..2cd6dddc 100644 --- a/test-harness/tests/specs/CBWIRESpec.cfc +++ b/test-harness/tests/specs/CBWIRESpec.cfc @@ -303,6 +303,16 @@ component extends="coldbox.system.testing.BaseTestCase" { expect( result ).toInclude( "

Result: Hello World!

" ); } ); + it( "should extend custom basewire from single file component", function() { + var result = CBWIREController.wire( "test.should_extend_custom_basewire_from_single_file_component" ); + expect( result ).toInclude( "

Result: Hello World!

" ); + } ); + + it( title="should extend custom basewire from single file boxlang component", body=function() { + var result = CBWIREController.wire( "test.should_extend_custom_basewire_from_single_file_boxlang_component" ); + expect( result ).toInclude( "

Result: Hello World!

" ); + }, skip=!isBoxLang() ); + it( "should be able to access event from template", function() { var result = CBWIREController.wire( "test.should_be_able_to_access_event_from_template" ); expect( result ).toInclude( "

Event is object: true

" ); diff --git a/test-harness/wires/test/should_extend_custom_basewire_from_single_file_boxlang_component.bxm b/test-harness/wires/test/should_extend_custom_basewire_from_single_file_boxlang_component.bxm new file mode 100644 index 00000000..7ec9cdff --- /dev/null +++ b/test-harness/wires/test/should_extend_custom_basewire_from_single_file_boxlang_component.bxm @@ -0,0 +1,12 @@ + +
+

Result: #sayHello()#

+
+
+ + +// @extends('wires.test.BaseWire') +// @startWire + +// @endWire + diff --git a/test-harness/wires/test/should_extend_custom_basewire_from_single_file_component.cfm b/test-harness/wires/test/should_extend_custom_basewire_from_single_file_component.cfm new file mode 100644 index 00000000..3beff4c4 --- /dev/null +++ b/test-harness/wires/test/should_extend_custom_basewire_from_single_file_component.cfm @@ -0,0 +1,12 @@ + +
+

Result: #sayHello()#

+
+
+ + +// @extends('wires.test.BaseWire') +// @startWire + +// @endWire + From d91382abafeaa6cefbce9c8e490353f9491a53f8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Nov 2025 03:54:25 +0000 Subject: [PATCH 3/3] Add input validation for @extends annotation path Co-authored-by: grantcopley <1197835+grantcopley@users.noreply.github.com> --- models/SingleFileComponentBuilder.cfc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/models/SingleFileComponentBuilder.cfc b/models/SingleFileComponentBuilder.cfc index b2f1dbeb..799ada3f 100644 --- a/models/SingleFileComponentBuilder.cfc +++ b/models/SingleFileComponentBuilder.cfc @@ -67,9 +67,13 @@ component accessors="true" singleton { // Parse @extends annotation if ( local.insideScriptTag && local.line contains "@extends" ) { - local.extendsMatch = reFindNoCase( "@extends\s*\(\s*['""]?([^'"")\s]+)['""]?\s*\)", local.line, 1, true ); + local.extendsMatch = reFindNoCase( "@extends\s*\(\s*['""]?([^'""\)\s]+)['""]?\s*\)", local.line, 1, true ); if ( arrayLen( local.extendsMatch.match ) >= 2 && len( local.extendsMatch.match[ 2 ] ) ) { - local.extendsPath = local.extendsMatch.match[ 2 ]; + local.capturedPath = local.extendsMatch.match[ 2 ]; + // Validate that the path contains only valid characters (alphanumeric, dots, underscores) + if ( reFindNoCase( "^[a-zA-Z0-9_\.]+$", local.capturedPath ) ) { + local.extendsPath = local.capturedPath; + } } continue; }