Skip to content

Commit 44ff5fd

Browse files
committed
fix: correctly handle 'as' prop name in Props detection
Fixed Props interface being marked as unused when using 'as' as a prop name. The compiler now distinguishes between import aliasing and property destructuring contexts. - Add context checking to prevent false positives - Add test coverage for 'as' prop scenarios
1 parent 89c80fe commit 44ff5fd

File tree

2 files changed

+108
-1
lines changed

2 files changed

+108
-1
lines changed

internal/js_scanner/js_scanner.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,12 @@ func isKeyword(value []byte) bool {
229229
return js.Keywords[string(value)] != 0
230230
}
231231

232+
// isPropsAliasing checks if we're in a Props aliasing context (import { Props as X })
233+
// rather than destructuring with 'as' property ({ as: Component })
234+
func isPropsAliasing(idents []string) bool {
235+
return len(idents) > 0 && idents[len(idents)-1] == "Props"
236+
}
237+
232238
func HoistImports(source []byte) HoistedScripts {
233239
imports := make([][]byte, 0)
234240
importLocs := make([]loc.Loc, 0)
@@ -340,7 +346,8 @@ outer:
340346
if js.IsIdentifier(token) {
341347
if isKeyword(value) {
342348
// fix(#814): fix Props detection when using `{ Props as SomethingElse }`
343-
if ident == "Props" && string(value) == "as" {
349+
// fix(#927): only reset Props when 'as' follows 'Props' in the same context
350+
if ident == "Props" && string(value) == "as" && isPropsAliasing(idents) {
344351
start = 0
345352
ident = defaultPropType
346353
idents = make([]string, 0)

internal/js_scanner/js_scanner_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,3 +775,103 @@ func TestGetObjectKeys(t *testing.T) {
775775
})
776776
}
777777
}
778+
779+
// propsTestCase represents a test case for GetPropsType
780+
type propsTestCase struct {
781+
name string
782+
source string
783+
want Props
784+
}
785+
786+
// makeProps is a helper to create Props structs concisely
787+
func makeProps(ident string, statement string, generics string) Props {
788+
return Props{
789+
Ident: ident,
790+
Statement: statement,
791+
Generics: generics,
792+
}
793+
}
794+
795+
// getPropsTypeTestCases returns all test cases for GetPropsType
796+
func getPropsTypeTestCases() []propsTestCase {
797+
const defaultType = "Record<string, any>"
798+
799+
return []propsTestCase{
800+
// Basic cases
801+
{
802+
name: "no props",
803+
source: `const foo = "bar"`,
804+
want: makeProps(defaultType, "", ""),
805+
},
806+
{
807+
name: "interface Props",
808+
source: `interface Props {
809+
foo: string;
810+
}`,
811+
want: makeProps("Props", "", ""),
812+
},
813+
{
814+
name: "type Props",
815+
source: `type Props = {
816+
foo: string;
817+
}`,
818+
want: makeProps("Props", "", ""),
819+
},
820+
821+
// Generics
822+
{
823+
name: "Props with generics",
824+
source: `interface Props<T> {
825+
foo: T;
826+
}`,
827+
want: makeProps("Props", "<T>", "<T>"),
828+
},
829+
830+
// Issue #927: 'as' prop name handling
831+
{
832+
name: "destructuring with 'as' prop name without type assertion - issue #927",
833+
source: `interface Props {
834+
as?: string;
835+
href?: string;
836+
}
837+
const { as: Component, href } = Astro.props;`,
838+
want: makeProps("Props", "", ""),
839+
},
840+
{
841+
name: "destructuring with 'as' prop name with type assertion",
842+
source: `interface Props {
843+
as?: string;
844+
href?: string;
845+
}
846+
const { as: Component, href } = Astro.props as Props;`,
847+
want: makeProps("Props", "", ""),
848+
},
849+
}
850+
}
851+
852+
// checks if two Props are equal and reports errors
853+
func assertPropsEqual(t *testing.T, got, want Props, source string) {
854+
t.Helper()
855+
856+
if got.Ident != want.Ident {
857+
t.Errorf("Ident mismatch:\n got: %q\n want: %q", got.Ident, want.Ident)
858+
t.Logf("Source:\n%s", source)
859+
}
860+
if got.Statement != want.Statement {
861+
t.Errorf("Statement mismatch:\n got: %q\n want: %q", got.Statement, want.Statement)
862+
}
863+
if got.Generics != want.Generics {
864+
t.Errorf("Generics mismatch:\n got: %q\n want: %q", got.Generics, want.Generics)
865+
}
866+
}
867+
868+
func TestGetPropsType(t *testing.T) {
869+
tests := getPropsTypeTestCases()
870+
871+
for _, tt := range tests {
872+
t.Run(tt.name, func(t *testing.T) {
873+
got := GetPropsType([]byte(tt.source))
874+
assertPropsEqual(t, got, tt.want, tt.source)
875+
})
876+
}
877+
}

0 commit comments

Comments
 (0)