Skip to content

Commit d4216ce

Browse files
refactor: address comments.
1 parent 89ee766 commit d4216ce

1 file changed

Lines changed: 75 additions & 21 deletions

File tree

src/modules/render-runtime.js

Lines changed: 75 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -586,40 +586,94 @@ export const createRenderRuntimeController = ({
586586

587587
const { byId, byModuleKey } = buildWorkspaceTabLookup(tabs)
588588
const visited = new Set()
589+
const visiting = new Set()
590+
const visitStack = []
589591
const dependencyOrder = []
592+
const importsByTabId = new Map()
590593

591-
const visit = tab => {
592-
if (!tab || visited.has(tab.id)) {
593-
return
594+
const toCycleTabLabel = tab => toTabModuleKey(tab) || tab.id
595+
596+
const getParsedImportsForTab = tab => {
597+
if (!tab || typeof tab.id !== 'string' || tab.id.length === 0) {
598+
return []
594599
}
595600

596-
visited.add(tab.id)
601+
if (importsByTabId.has(tab.id)) {
602+
return importsByTabId.get(tab.id)
603+
}
597604

598605
const source = typeof tab.content === 'string' ? tab.content : ''
599606
const imports = parseWorkspaceImports({ source, transformJsxSource })
607+
importsByTabId.set(tab.id, imports)
608+
return imports
609+
}
610+
611+
const visit = (tab, { viaSpecifier = '' } = {}) => {
612+
if (!tab) {
613+
return
614+
}
600615

601-
workspaceGraphCache.upsert({
602-
tabId: tab.id,
603-
imports: imports.map(entry => entry?.source).filter(Boolean),
604-
lastUpdated: Date.now(),
616+
if (visiting.has(tab.id)) {
617+
const cycleStartIndex = visitStack.findIndex(entry => entry.id === tab.id)
618+
const cycleEntries =
619+
cycleStartIndex >= 0 ? visitStack.slice(cycleStartIndex) : [...visitStack]
620+
const cycleLabels = [
621+
...cycleEntries.map(entry => entry.label),
622+
toCycleTabLabel(tab),
623+
].join(' -> ')
624+
const importPath = [
625+
...cycleEntries.slice(1).map(entry => entry.viaSpecifier),
626+
viaSpecifier,
627+
]
628+
.filter(Boolean)
629+
.join(' -> ')
630+
631+
const importHint = importPath ? ` Import chain: ${importPath}.` : ''
632+
throw new Error(
633+
`Preview entry contains circular workspace import: ${cycleLabels}.${importHint}`,
634+
)
635+
}
636+
637+
if (visited.has(tab.id)) {
638+
return
639+
}
640+
641+
visiting.add(tab.id)
642+
visitStack.push({
643+
id: tab.id,
644+
label: toCycleTabLabel(tab),
645+
viaSpecifier,
605646
})
647+
try {
648+
const imports = getParsedImportsForTab(tab)
606649

607-
for (const entry of imports) {
608-
if (!isRelativeSpecifier(entry?.source)) {
609-
continue
610-
}
650+
workspaceGraphCache.upsert({
651+
tabId: tab.id,
652+
imports: imports.map(entry => entry?.source).filter(Boolean),
653+
lastUpdated: Date.now(),
654+
})
611655

612-
const target = byModuleKey.get(entry.source)
613-
if (!target) {
614-
throw new Error(
615-
`Preview entry references missing workspace module: ${entry.source}`,
616-
)
656+
for (const entry of imports) {
657+
if (!isRelativeSpecifier(entry?.source)) {
658+
continue
659+
}
660+
661+
const target = byModuleKey.get(entry.source)
662+
if (!target) {
663+
throw new Error(
664+
`Preview entry references missing workspace module: ${entry.source}`,
665+
)
666+
}
667+
668+
visit(target, { viaSpecifier: entry.source })
617669
}
618670

619-
visit(target)
671+
visited.add(tab.id)
672+
dependencyOrder.push(tab.id)
673+
} finally {
674+
visitStack.pop()
675+
visiting.delete(tab.id)
620676
}
621-
622-
dependencyOrder.push(tab.id)
623677
}
624678

625679
visit(entryTab)
@@ -634,7 +688,7 @@ export const createRenderRuntimeController = ({
634688
}
635689

636690
const source = typeof tab.content === 'string' ? tab.content : ''
637-
const imports = parseWorkspaceImports({ source, transformJsxSource })
691+
const imports = getParsedImportsForTab(tab)
638692
const aliases = createRelativeImportAliases(imports)
639693
const withoutRelativeImports = stripImportDeclarationsBy(source, imports, entry =>
640694
isRelativeSpecifier(entry?.source),

0 commit comments

Comments
 (0)