@tanstack/devtools 0.6.20 → 0.6.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk/{XFQ6P775.js → XF4JFOLU.js} +15 -0
- package/dist/dev.js +13 -3
- package/dist/devtools/{YM72BEIK.js → MBQPV7BO.js} +1907 -70
- package/dist/devtools/{6XAY2RKM.js → YRFZDV5N.js} +2260 -169
- package/dist/index.js +13 -3
- package/dist/server.js +9 -2
- package/package.json +6 -3
- package/src/components/source-inspector.tsx +158 -0
- package/src/context/devtools-context.tsx +24 -1
- package/src/core.tsx +15 -1
- package/src/devtools.tsx +3 -28
- package/src/styles/use-styles.ts +829 -0
- package/src/tabs/marketplace/card-utils.test.ts +219 -0
- package/src/tabs/marketplace/card-utils.ts +85 -0
- package/src/tabs/marketplace/marketplace-header.tsx +54 -0
- package/src/tabs/marketplace/plugin-card.tsx +165 -0
- package/src/tabs/marketplace/plugin-section.tsx +51 -0
- package/src/tabs/marketplace/plugin-utils.test.ts +518 -0
- package/src/tabs/marketplace/plugin-utils.ts +248 -0
- package/src/tabs/marketplace/settings-panel.tsx +41 -0
- package/src/tabs/marketplace/tag-filters.tsx +35 -0
- package/src/tabs/marketplace/types.ts +47 -0
- package/src/tabs/plugin-marketplace.tsx +346 -0
- package/src/tabs/plugin-registry.ts +222 -0
- package/src/tabs/plugins-tab.tsx +112 -65
- package/src/tabs/semver-utils.test.ts +218 -0
- package/src/tabs/semver-utils.ts +114 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { getAllPluginMetadata } from '../plugin-registry'
|
|
2
|
+
import { satisfiesVersionRange } from '../semver-utils'
|
|
3
|
+
import type { PackageJson } from '@tanstack/devtools-client'
|
|
4
|
+
import type { ActionType, FRAMEWORKS, PluginCard, PluginSection } from './types'
|
|
5
|
+
|
|
6
|
+
export const detectFramework = (
|
|
7
|
+
pkg: PackageJson,
|
|
8
|
+
frameworks: typeof FRAMEWORKS,
|
|
9
|
+
): string => {
|
|
10
|
+
const allDeps = {
|
|
11
|
+
...pkg.dependencies,
|
|
12
|
+
...pkg.devDependencies,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Map of framework to their actual package names
|
|
16
|
+
const frameworkPackageMap: Record<string, Array<string>> = {
|
|
17
|
+
react: ['react', 'react-dom'],
|
|
18
|
+
vue: ['vue', '@vue/core'],
|
|
19
|
+
solid: ['solid-js'],
|
|
20
|
+
svelte: ['svelte'],
|
|
21
|
+
angular: ['@angular/core'],
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Check for actual framework packages
|
|
25
|
+
for (const framework of frameworks) {
|
|
26
|
+
const frameworkPackages = frameworkPackageMap[framework]
|
|
27
|
+
if (frameworkPackages && frameworkPackages.some((pkg) => allDeps[pkg])) {
|
|
28
|
+
return framework
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return 'unknown'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const isPluginRegistered = (
|
|
36
|
+
registeredPlugins: Set<string>,
|
|
37
|
+
packageName: string,
|
|
38
|
+
pluginName: string,
|
|
39
|
+
framework: string,
|
|
40
|
+
pluginId?: string,
|
|
41
|
+
): boolean => {
|
|
42
|
+
// If a custom pluginId is provided, use it for matching
|
|
43
|
+
if (pluginId) {
|
|
44
|
+
return Array.from(registeredPlugins).some((id) => {
|
|
45
|
+
const idLower = id.toLowerCase()
|
|
46
|
+
const pluginIdLower = pluginId.toLowerCase()
|
|
47
|
+
// Match if the registered id starts with the pluginId or contains it
|
|
48
|
+
return (
|
|
49
|
+
idLower.startsWith(pluginIdLower) || idLower.includes(pluginIdLower)
|
|
50
|
+
)
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Direct match on package name
|
|
55
|
+
if (registeredPlugins.has(packageName)) return true
|
|
56
|
+
|
|
57
|
+
// Check if any registered plugin name contains the key parts
|
|
58
|
+
// Extract meaningful words from pluginName (split by common delimiters)
|
|
59
|
+
const pluginWords = pluginName
|
|
60
|
+
.toLowerCase()
|
|
61
|
+
.split(/[-_/@]/)
|
|
62
|
+
.filter((word) => word.length > 0)
|
|
63
|
+
const frameworkPart = framework.toLowerCase()
|
|
64
|
+
|
|
65
|
+
return Array.from(registeredPlugins).some((id) => {
|
|
66
|
+
const idLower = id.toLowerCase()
|
|
67
|
+
|
|
68
|
+
// Match if registered id contains the full pluginName
|
|
69
|
+
if (idLower.includes(pluginName.toLowerCase())) {
|
|
70
|
+
return true
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Match if multiple key words from pluginName are found in the registered id
|
|
74
|
+
const matchedWords = pluginWords.filter((word) => idLower.includes(word))
|
|
75
|
+
|
|
76
|
+
// If we match multiple words, it's likely the same plugin
|
|
77
|
+
if (matchedWords.length >= 2) {
|
|
78
|
+
return true
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Match if framework and at least one plugin word match
|
|
82
|
+
if (idLower.includes(frameworkPart) && matchedWords.length >= 1) {
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return false
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const buildPluginCards = (
|
|
91
|
+
pkg: PackageJson,
|
|
92
|
+
currentFramework: string,
|
|
93
|
+
registeredPlugins: Set<string>,
|
|
94
|
+
existingCards: Array<PluginCard>,
|
|
95
|
+
): Array<PluginCard> => {
|
|
96
|
+
const allDeps = {
|
|
97
|
+
...pkg.dependencies,
|
|
98
|
+
...pkg.devDependencies,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const allCards: Array<PluginCard> = []
|
|
102
|
+
|
|
103
|
+
// Iterate through all plugins in the registry
|
|
104
|
+
const allPlugins = getAllPluginMetadata()
|
|
105
|
+
|
|
106
|
+
allPlugins.forEach((metadata) => {
|
|
107
|
+
const devtoolsPackage = metadata.packageName
|
|
108
|
+
|
|
109
|
+
// Check if plugin's framework matches current framework or is 'other' (framework-agnostic)
|
|
110
|
+
const isCurrentFramework =
|
|
111
|
+
metadata.framework === currentFramework || metadata.framework === 'other'
|
|
112
|
+
|
|
113
|
+
// Extract package name from devtools package
|
|
114
|
+
// For @tanstack/react-query-devtools, the base package is @tanstack/react-query
|
|
115
|
+
const requiredPackageName = metadata.requires?.packageName
|
|
116
|
+
|
|
117
|
+
const hasPackage = requiredPackageName
|
|
118
|
+
? !!allDeps[requiredPackageName]
|
|
119
|
+
: false
|
|
120
|
+
const hasDevtools = !!allDeps[devtoolsPackage]
|
|
121
|
+
|
|
122
|
+
// Check version requirements based on the requires field
|
|
123
|
+
let versionInfo: PluginCard['versionInfo'] | undefined
|
|
124
|
+
if (hasPackage && metadata.requires) {
|
|
125
|
+
const currentVersion = requiredPackageName
|
|
126
|
+
? allDeps[requiredPackageName]
|
|
127
|
+
: undefined
|
|
128
|
+
if (currentVersion) {
|
|
129
|
+
const versionCheck = satisfiesVersionRange(
|
|
130
|
+
currentVersion,
|
|
131
|
+
metadata.requires.minVersion,
|
|
132
|
+
metadata.requires.maxVersion,
|
|
133
|
+
)
|
|
134
|
+
versionInfo = {
|
|
135
|
+
current: currentVersion,
|
|
136
|
+
required: metadata.requires.minVersion,
|
|
137
|
+
satisfied: versionCheck.satisfied,
|
|
138
|
+
reason: versionCheck.reason,
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Check if plugin is registered
|
|
144
|
+
const isRegistered = isPluginRegistered(
|
|
145
|
+
registeredPlugins,
|
|
146
|
+
devtoolsPackage,
|
|
147
|
+
metadata.packageName,
|
|
148
|
+
metadata.framework,
|
|
149
|
+
metadata.pluginId,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
// Determine action type
|
|
153
|
+
let actionType: ActionType
|
|
154
|
+
if (!isCurrentFramework) {
|
|
155
|
+
actionType = 'wrong-framework'
|
|
156
|
+
} else if (metadata.requires && !hasPackage) {
|
|
157
|
+
actionType = 'requires-package'
|
|
158
|
+
} else if (versionInfo && !versionInfo.satisfied) {
|
|
159
|
+
// Version doesn't meet requirements - need to bump
|
|
160
|
+
actionType = 'bump-version'
|
|
161
|
+
} else if (hasDevtools && isRegistered) {
|
|
162
|
+
actionType = 'already-installed'
|
|
163
|
+
} else if (hasDevtools && !isRegistered) {
|
|
164
|
+
actionType = 'add-to-devtools'
|
|
165
|
+
} else if (!hasDevtools && metadata.requires && hasPackage) {
|
|
166
|
+
// Requirement is met but devtools package not installed
|
|
167
|
+
actionType = 'install-devtools'
|
|
168
|
+
} else if (!hasDevtools) {
|
|
169
|
+
actionType = 'install'
|
|
170
|
+
} else {
|
|
171
|
+
// Fallback - should not reach here
|
|
172
|
+
actionType = 'install'
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Find existing card to preserve status
|
|
176
|
+
const existing = existingCards.find(
|
|
177
|
+
(c) => c.devtoolsPackage === devtoolsPackage,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
allCards.push({
|
|
181
|
+
requiredPackageName: requiredPackageName || '',
|
|
182
|
+
devtoolsPackage,
|
|
183
|
+
framework: metadata.framework,
|
|
184
|
+
hasPackage,
|
|
185
|
+
hasDevtools,
|
|
186
|
+
isRegistered,
|
|
187
|
+
actionType,
|
|
188
|
+
status: existing?.status || 'idle',
|
|
189
|
+
error: existing?.error,
|
|
190
|
+
isCurrentFramework,
|
|
191
|
+
metadata,
|
|
192
|
+
versionInfo,
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
return allCards
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const groupIntoSections = (
|
|
200
|
+
allCards: Array<PluginCard>,
|
|
201
|
+
): Array<PluginSection> => {
|
|
202
|
+
const sections: Array<PluginSection> = []
|
|
203
|
+
|
|
204
|
+
// Add Active Plugins section
|
|
205
|
+
const activeCards = allCards.filter(
|
|
206
|
+
(c) => c.actionType === 'already-installed' && c.isRegistered,
|
|
207
|
+
)
|
|
208
|
+
if (activeCards.length > 0) {
|
|
209
|
+
sections.push({
|
|
210
|
+
id: 'active',
|
|
211
|
+
displayName: '✓ Active Plugins',
|
|
212
|
+
cards: activeCards,
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Add Featured section
|
|
217
|
+
const featuredCards = allCards.filter(
|
|
218
|
+
(c) =>
|
|
219
|
+
c.metadata?.featured &&
|
|
220
|
+
c.actionType !== 'already-installed' &&
|
|
221
|
+
c.isCurrentFramework, // Only show featured plugins for current framework
|
|
222
|
+
)
|
|
223
|
+
if (featuredCards.length > 0) {
|
|
224
|
+
sections.push({
|
|
225
|
+
id: 'featured',
|
|
226
|
+
displayName: '⭐ Featured',
|
|
227
|
+
cards: featuredCards,
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Add Available section - all plugins for current framework (TanStack + third-party)
|
|
232
|
+
const availableCards = allCards.filter(
|
|
233
|
+
(c) =>
|
|
234
|
+
c.isCurrentFramework &&
|
|
235
|
+
c.actionType !== 'already-installed' &&
|
|
236
|
+
!c.metadata?.featured, // Not featured (already in featured section)
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
if (availableCards.length > 0) {
|
|
240
|
+
sections.push({
|
|
241
|
+
id: 'available',
|
|
242
|
+
displayName: 'Available Plugins',
|
|
243
|
+
cards: availableCards,
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return sections
|
|
248
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Show } from 'solid-js'
|
|
2
|
+
import { Checkbox, CloseIcon } from '@tanstack/devtools-ui'
|
|
3
|
+
import { useStyles } from '../../styles/use-styles'
|
|
4
|
+
import type { Accessor, Setter } from 'solid-js'
|
|
5
|
+
|
|
6
|
+
interface SettingsPanelProps {
|
|
7
|
+
isOpen: Accessor<boolean>
|
|
8
|
+
onClose: () => void
|
|
9
|
+
showActivePlugins: Accessor<boolean>
|
|
10
|
+
setShowActivePlugins: Setter<boolean>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const SettingsPanel = (props: SettingsPanelProps) => {
|
|
14
|
+
const styles = useStyles()
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Show when={props.isOpen()}>
|
|
18
|
+
<div class={styles().pluginMarketplaceSettingsPanel}>
|
|
19
|
+
<div class={styles().pluginMarketplaceSettingsPanelHeader}>
|
|
20
|
+
<h3 class={styles().pluginMarketplaceSettingsPanelTitle}>
|
|
21
|
+
Marketplace Settings
|
|
22
|
+
</h3>
|
|
23
|
+
<button
|
|
24
|
+
class={styles().pluginMarketplaceSettingsPanelClose}
|
|
25
|
+
onClick={props.onClose}
|
|
26
|
+
>
|
|
27
|
+
<CloseIcon />
|
|
28
|
+
</button>
|
|
29
|
+
</div>
|
|
30
|
+
<div class={styles().pluginMarketplaceSettingsPanelContent}>
|
|
31
|
+
<Checkbox
|
|
32
|
+
label="Show active plugins"
|
|
33
|
+
description="Display installed plugins in a separate section"
|
|
34
|
+
checked={props.showActivePlugins()}
|
|
35
|
+
onChange={(checked) => props.setShowActivePlugins(checked)}
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</Show>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { For, Show } from 'solid-js'
|
|
2
|
+
import { useStyles } from '../../styles/use-styles'
|
|
3
|
+
import type { Accessor } from 'solid-js'
|
|
4
|
+
|
|
5
|
+
interface TagFiltersProps {
|
|
6
|
+
tags: Accessor<Array<string>>
|
|
7
|
+
selectedTags: Accessor<Set<string>>
|
|
8
|
+
onToggleTag: (tag: string) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const TagFilters = (props: TagFiltersProps) => {
|
|
12
|
+
const styles = useStyles()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Show when={props.tags().length > 0}>
|
|
16
|
+
<div class={styles().pluginMarketplaceTagsContainer}>
|
|
17
|
+
<For each={props.tags()}>
|
|
18
|
+
{(tag) => (
|
|
19
|
+
<button
|
|
20
|
+
class={styles().pluginMarketplaceTagButton}
|
|
21
|
+
classList={{
|
|
22
|
+
[styles().pluginMarketplaceTagButtonActive]: props
|
|
23
|
+
.selectedTags()
|
|
24
|
+
.has(tag),
|
|
25
|
+
}}
|
|
26
|
+
onClick={() => props.onToggleTag(tag)}
|
|
27
|
+
>
|
|
28
|
+
{tag}
|
|
29
|
+
</button>
|
|
30
|
+
)}
|
|
31
|
+
</For>
|
|
32
|
+
</div>
|
|
33
|
+
</Show>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { PluginMetadata } from '../plugin-registry'
|
|
2
|
+
|
|
3
|
+
type InstallStatus = 'idle' | 'installing' | 'success' | 'error'
|
|
4
|
+
|
|
5
|
+
export type ActionType =
|
|
6
|
+
| 'install'
|
|
7
|
+
| 'install-devtools'
|
|
8
|
+
| 'add-to-devtools'
|
|
9
|
+
| 'requires-package'
|
|
10
|
+
| 'wrong-framework'
|
|
11
|
+
| 'already-installed'
|
|
12
|
+
| 'version-mismatch'
|
|
13
|
+
| 'bump-version'
|
|
14
|
+
|
|
15
|
+
export interface PluginCard {
|
|
16
|
+
requiredPackageName: string
|
|
17
|
+
devtoolsPackage: string
|
|
18
|
+
framework: string
|
|
19
|
+
hasPackage: boolean
|
|
20
|
+
hasDevtools: boolean
|
|
21
|
+
isRegistered: boolean
|
|
22
|
+
actionType: ActionType
|
|
23
|
+
status: InstallStatus
|
|
24
|
+
error?: string
|
|
25
|
+
isCurrentFramework: boolean
|
|
26
|
+
metadata?: PluginMetadata
|
|
27
|
+
versionInfo?: {
|
|
28
|
+
current: string
|
|
29
|
+
required?: string
|
|
30
|
+
satisfied: boolean
|
|
31
|
+
reason?: string
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface PluginSection {
|
|
36
|
+
id: string
|
|
37
|
+
displayName: string
|
|
38
|
+
cards: Array<PluginCard>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const FRAMEWORKS = [
|
|
42
|
+
'react',
|
|
43
|
+
'solid',
|
|
44
|
+
'vue',
|
|
45
|
+
'svelte',
|
|
46
|
+
'angular',
|
|
47
|
+
] as const
|