Add deprecation guidance and schema warnings

This commit is contained in:
2026-01-18 12:49:03 +00:00
parent 1261c3e44d
commit 571fe3ef2c
4 changed files with 76 additions and 2 deletions

View File

@@ -229,6 +229,29 @@ Priority for migration:
3. Components with good atomic design
4. Components without complex state management
## Deprecation & JSON Migration Process
Use this process when retiring legacy components or renaming JSON component types.
### Deprecation workflow
1. **Assess usage**: Identify schemas and component usage (search in `src/config/schemas` or project JSON). Flag any external consumers.
2. **Define a replacement**: Ensure a supported JSON-safe replacement exists (or create a wrapper) and document prop differences.
3. **Mark deprecated in the registry**: Update `json-components-registry.json` with `"status": "deprecated"` and optional `"deprecated"` metadata:
- `replacedBy`: the new component type to use.
- `message`: extra guidance for migrations.
4. **Update definitions**: If needed, adjust `src/lib/component-definitions.ts` to align defaults and prop expectations for the replacement.
5. **Communicate the change**: Add a note to release documentation or changelog and note the replacement.
6. **Set a removal window**: Target the next minor/major release for removal once migration is complete.
### JSON migration checklist
- [ ] Replace deprecated component types in schemas with their replacements.
- [ ] Validate props against the replacement component definition.
- [ ] Run JSON renderer previews to confirm layout and bindings.
- [ ] Remove any legacy bindings or props that are no longer supported.
### Runtime warning mechanism
The schema renderer emits a `console.warn` when a deprecated component type appears in a JSON schema. This warning uses the metadata in `json-components-registry.json` to suggest replacements and highlight remediation guidance.
## Related Documentation
- [PRD.md](./PRD.md) - Product requirements document

View File

@@ -421,8 +421,12 @@
"category": "input",
"canHaveChildren": false,
"description": "Multi-line text input",
"status": "supported",
"source": "atoms"
"status": "deprecated",
"source": "atoms",
"deprecated": {
"replacedBy": "Textarea",
"message": "Prefer the shadcn/ui Textarea component for JSON schemas."
}
},
{
"type": "Toggle",

View File

@@ -47,12 +47,19 @@ interface JsonRegistryEntry {
type?: string
export?: string
source?: string
status?: string
deprecated?: DeprecatedComponentInfo
}
interface JsonComponentRegistry {
components?: JsonRegistryEntry[]
}
export interface DeprecatedComponentInfo {
replacedBy?: string
message?: string
}
const jsonRegistry = jsonComponentsRegistry as JsonComponentRegistry
const buildRegistryFromNames = (
@@ -69,6 +76,19 @@ const buildRegistryFromNames = (
}
const jsonRegistryEntries = jsonRegistry.components ?? []
const deprecatedComponentInfo = jsonRegistryEntries.reduce<Record<string, DeprecatedComponentInfo>>(
(acc, entry) => {
const entryName = entry.export ?? entry.name ?? entry.type
if (!entryName) {
return acc
}
if (entry.status === 'deprecated' || entry.deprecated) {
acc[entryName] = entry.deprecated ?? {}
}
return acc
},
{}
)
const atomRegistryNames = jsonRegistryEntries
.filter((entry) => entry.source === 'atoms')
.map((entry) => entry.export ?? entry.name ?? entry.type)
@@ -237,3 +257,7 @@ export function getUIComponent(type: string): ComponentType<any> | string | null
export function hasComponent(type: string): boolean {
return type in uiComponentRegistry
}
export function getDeprecatedComponentInfo(type: string): DeprecatedComponentInfo | null {
return deprecatedComponentInfo[type] ?? null
}

View File

@@ -2,6 +2,7 @@ import { createElement, type ComponentType, type ReactNode } from 'react'
import { cn } from '@/lib/utils'
import { Component as ComponentSchema, Layout } from '@/schemas/ui-schema'
import { useDataBinding, useEventHandlers, useComponentRegistry } from '@/hooks/ui'
import { getDeprecatedComponentInfo } from '@/lib/json-ui/component-registry'
interface SchemaRendererProps {
schema: ComponentSchema
@@ -15,6 +16,26 @@ interface LayoutRendererProps {
children: ReactNode
}
const warnedDeprecatedComponents = new Set<string>()
const warnDeprecatedComponent = (schema: ComponentSchema) => {
const deprecatedInfo = getDeprecatedComponentInfo(schema.type)
if (!deprecatedInfo || warnedDeprecatedComponents.has(schema.type)) {
return
}
const idSuffix = schema.id ? ` (id: ${schema.id})` : ''
const replacementHint = deprecatedInfo.replacedBy
? ` Replace with "${deprecatedInfo.replacedBy}".`
: ''
const extraMessage = deprecatedInfo.message ? ` ${deprecatedInfo.message}` : ''
console.warn(
`[SchemaRenderer] Deprecated component "${schema.type}" detected in schema${idSuffix}.${replacementHint}${extraMessage}`
)
warnedDeprecatedComponents.add(schema.type)
}
function LayoutRenderer({ layout, children }: LayoutRendererProps) {
const getLayoutClasses = () => {
const classes: string[] = []
@@ -85,6 +106,8 @@ export function SchemaRenderer({ schema, data, functions = {}, componentRegistry
)
}
warnDeprecatedComponent(schema)
const props = resolveProps(schema.props || {})
const events = resolveEvents(schema.events)
const combinedProps = { ...props, ...events }