Skip to content

Commit f526d3d

Browse files
committed
Move isCompoundComponent to its own function
1 parent 5403fd4 commit f526d3d

File tree

5 files changed

+116
-79
lines changed

5 files changed

+116
-79
lines changed

packages/common/refresh-runtime.js

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -534,16 +534,6 @@ function isLikelyComponentType(type) {
534534
// Definitely React components.
535535
return true
536536
default:
537-
// Check if this is a compound component (object with all properties being React components)
538-
if (isPlainObject(type)) {
539-
const keys = Object.keys(type)
540-
if (
541-
keys.length > 0 &&
542-
keys.every((key) => isLikelyComponentType(type[key]))
543-
) {
544-
return true
545-
}
546-
}
547537
return false
548538
}
549539
}
@@ -555,6 +545,15 @@ function isLikelyComponentType(type) {
555545
}
556546
}
557547

548+
function isCompoundComponent(type) {
549+
if (!isPlainObject(type)) return false
550+
const keys = Object.keys(type)
551+
for (const key of keys) {
552+
if (!isLikelyComponentType(type[key])) return false
553+
}
554+
return true
555+
}
556+
558557
function isPlainObject(obj) {
559558
return (
560559
Object.prototype.toString.call(obj) === '[object Object]' &&
@@ -582,23 +581,12 @@ export function registerExportsForReactRefresh(filename, moduleExports) {
582581
// The register function has an identity check to not register twice the same component,
583582
// so this is safe to not used the same key here.
584583
register(exportValue, filename + ' export ' + key)
585-
586-
// If it's a compound component (plain object with component properties),
587-
// also register the individual components
588-
if (
589-
typeof exportValue === 'object' &&
590-
exportValue != null &&
591-
isPlainObject(exportValue) &&
592-
Object.keys(exportValue).every((subKey) =>
593-
isLikelyComponentType(exportValue[subKey]),
584+
} else if (isCompoundComponent(exportValue)) {
585+
for (const subKey in exportValue) {
586+
register(
587+
exportValue[subKey],
588+
filename + ' export ' + key + '$' + subKey,
594589
)
595-
) {
596-
for (const subKey in exportValue) {
597-
register(
598-
exportValue[subKey],
599-
filename + ' export ' + key + '$' + subKey,
600-
)
601-
}
602590
}
603591
}
604592
}
@@ -653,6 +641,7 @@ export function validateRefreshBoundaryAndEnqueueUpdate(
653641
(key, value) => {
654642
hasExports = true
655643
if (isLikelyComponentType(value)) return true
644+
if (isCompoundComponent(value)) return true
656645
return prevExports[key] === nextExports[key]
657646
},
658647
)
Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
import { expect, test } from 'vitest'
2-
import { editFile, page, untilBrowserLogAfter } from '~utils'
2+
import { editFile, isServe, page, untilBrowserLogAfter } from '~utils'
33

44
test('should render compound components', async () => {
55
expect(await page.textContent('h1')).toMatch('Compound Components HMR Test')
66
expect(await page.textContent('h3')).toMatch('Accordion Root')
77
})
88

9-
test('compound components should use HMR instead of full reload', async () => {
10-
const logs = await untilBrowserLogAfter(() => {
11-
editFile('src/Accordion.tsx', (code) =>
12-
code.replace('Accordion Root', 'Accordion Root Updated'),
9+
if (isServe) {
10+
test('compound components should use HMR instead of full reload', async () => {
11+
const logs = await untilBrowserLogAfter(
12+
() =>
13+
editFile('src/Accordion.tsx', (code) =>
14+
code.replace('Accordion Root', 'Accordion Root Updated'),
15+
),
16+
/\[vite\]/,
1317
)
14-
}, /\[vite\]/)
1518

16-
expect(logs).toContain('[vite] hot updated: /src/Accordion.tsx')
17-
expect(logs).not.toContain('[vite] invalidate')
19+
expect(logs).toContain('[vite] hot updated: /src/Accordion.tsx')
20+
expect(logs).not.toContain('[vite] invalidate')
1821

19-
// revert changes
20-
editFile('src/Accordion.tsx', (code) =>
21-
code.replace('Accordion Root Updated', 'Accordion Root'),
22-
)
23-
})
22+
// revert changes
23+
editFile('src/Accordion.tsx', (code) =>
24+
code.replace('Accordion Root Updated', 'Accordion Root'),
25+
)
26+
})
27+
}

playground/compound-components/package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
"serve": "vite preview"
1010
},
1111
"dependencies": {
12-
"react": "^18.3.1",
13-
"react-dom": "^18.3.1"
12+
"react": "^19.1.0",
13+
"react-dom": "^19.1.0"
1414
},
1515
"devDependencies": {
16-
"@types/react": "^18.3.5",
17-
"@types/react-dom": "^18.3.1",
16+
"@types/react": "^19.1.8",
17+
"@types/react-dom": "^19.1.6",
1818
"@vitejs/plugin-react": "workspace:*",
19-
"typescript": "^5.5.3",
20-
"vite": "^6.0.5"
19+
"typescript": "^5.8.3",
20+
"vite": "^7.0.4"
2121
}
2222
}

playground/compound-components/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@
2121
"noUnusedParameters": true,
2222
"noFallthroughCasesInSwitch": true
2323
},
24-
"include": ["src"]
24+
"include": ["src", "vite.config.ts"]
2525
}

pnpm-lock.yaml

Lines changed: 77 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)