@tanstack/devtools 0.6.19 → 0.6.21

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.
@@ -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