@@ -621,3 +621,102 @@ func TestDollarVariableInRangeBlock(t *testing.T) {
621621 })
622622 }
623623}
624+
625+ // TestDefineWithOnlyHTML tests that define blocks containing only HTML
626+ // (no template expressions) don't produce false "variable is never used" errors.
627+ // This reproduces the exact file from web/portfolio/portfolio-dialogs.html
628+ func TestDefineWithOnlyHTML (t * testing.T ) {
629+ source := `{{define "portfolio-dialogs" -}}
630+ <dialog id="sync-units-dialog" class="modal modal-sm">
631+ <header aria-label="Sync units dialog header">
632+ <h2>Sync Units</h2>
633+ <button type="button" class="modal-close">×</button>
634+ </header>
635+ <div class="modal-body">
636+ <p id="sync-units-description">Sync units for this portfolio.</p>
637+ <label class="checkbox-label">
638+ <input type="checkbox" id="sync-make-managed">
639+ Make all new, active units "managed"
640+ </label>
641+ <label class="checkbox-label hidden" id="sync-import-reservations-label">
642+ <input type="checkbox" id="sync-import-reservations" disabled>
643+ Immediately import reservations
644+ </label>
645+ </div>
646+ <footer>
647+ <button type="button" class="btn-secondary modal-close">Cancel</button>
648+ <button type="button" class="btn-primary" id="sync-units-confirm-btn" onclick="handleSyncUnitsConfirm()">Sync Units</button>
649+ </footer>
650+ </dialog>
651+
652+ <dialog id="sync-progress-dialog" class="modal modal-sm">
653+ <header aria-label="Sync progress dialog header">
654+ <h2>Syncing...</h2>
655+ </header>
656+ <div class="modal-body">
657+ <div class="loading-spinner"></div>
658+ <p>Please wait...</p>
659+ </div>
660+ </dialog>
661+ {{- end -}}`
662+
663+ root , parseErrs := template .ParseSingleFile ([]byte (source ))
664+ for _ , err := range parseErrs {
665+ t .Logf ("Parse error: %s" , err .GetError ())
666+ }
667+
668+ workspace := map [string ]* parser.GroupStatementNode {
669+ "test.html" : root ,
670+ }
671+
672+ results := template .DefinitionAnalysisWithinWorkspace (workspace )
673+
674+ for _ , result := range results {
675+ for _ , err := range result .Errs {
676+ errMsg := err .GetError ()
677+ if testutil .ContainsSubstring (errMsg , "variable is never used" ) {
678+ t .Errorf ("False positive 'variable is never used': %s" , errMsg )
679+ }
680+ }
681+ }
682+ }
683+
684+ // TestWorkspaceWithMultipleDefines tests workspace analysis doesn't produce
685+ // false "variable is never used" errors across multiple template files.
686+ func TestWorkspaceWithMultipleDefines (t * testing.T ) {
687+ // File 1: define with only HTML
688+ file1 := `{{define "dialogs" -}}
689+ <dialog id="test-dialog" class="modal">
690+ <h2>Test</h2>
691+ </dialog>
692+ {{- end -}}`
693+
694+ // File 2: define that uses template expressions
695+ file2 := `{{define "content" -}}
696+ <div>{{.Title}}</div>
697+ {{- end -}}`
698+
699+ // File 3: root template (no define wrapper)
700+ file3 := `<section>{{.Content}}</section>`
701+
702+ root1 , _ := template .ParseSingleFile ([]byte (file1 ))
703+ root2 , _ := template .ParseSingleFile ([]byte (file2 ))
704+ root3 , _ := template .ParseSingleFile ([]byte (file3 ))
705+
706+ workspace := map [string ]* parser.GroupStatementNode {
707+ "dialogs.html" : root1 ,
708+ "content.html" : root2 ,
709+ "page.html" : root3 ,
710+ }
711+
712+ results := template .DefinitionAnalysisWithinWorkspace (workspace )
713+
714+ for _ , result := range results {
715+ for _ , err := range result .Errs {
716+ errMsg := err .GetError ()
717+ if testutil .ContainsSubstring (errMsg , "variable is never used" ) {
718+ t .Errorf ("False positive 'variable is never used': %s" , errMsg )
719+ }
720+ }
721+ }
722+ }
0 commit comments