Architecture evaluation and build system optimization#289
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- Add ARCHITECTURE_EVALUATION.md (English) with detailed analysis - Add ARCHITECTURE_EVALUATION.zh-CN.md (Chinese translation) - Configure Turbo for parallel builds (3-5x faster) - Add shared TypeScript configurations (tsconfig.base.json, tsconfig.react.json, tsconfig.node.json) - Create developer onboarding script (scripts/setup.sh) - Update package.json to use Turbo for build, test, and lint - Update README with architecture documentation links - Update scripts/README.md with setup script documentation Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
- Added namespace field to all ComponentRegistry.register() calls in plugin packages
- Namespace follows pattern: 'plugin-{name}' matching package directory name
- Updated 20 component registrations across 12 plugin packages:
* plugin-aggrid (2 components)
* plugin-calendar (2 components)
* plugin-charts (3 components)
* plugin-chatbot (1 component)
* plugin-dashboard (2 components)
* plugin-editor (1 component)
* plugin-form (1 component)
* plugin-gantt (1 component)
* plugin-map (1 component)
* plugin-markdown (1 component)
* plugin-timeline (1 component)
* plugin-view (2 components)
- Skipped plugin-grid and plugin-kanban (already updated)
- Maintains all existing metadata fields
Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
…ify namespaces Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…optimization, and testing standards Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces architecture evaluation documentation and implements several of the recommended improvements: namespaced components, an initial plugin scope system, build/test tooling consolidation, and early support for lazy field registration.
Changes:
- Adds comprehensive architecture evaluation docs (English + Chinese) and a high-level summary, and surfaces them from the README.
- Introduces component namespaces, a typed plugin scope API, and core support for plugin isolation, while adding lazy/lighter-weight field registration primitives.
- Optimizes the build and test pipeline via Turbo, shared TypeScript configs, a setup script, CI workflow tweaks, and coverage thresholds.
Reviewed changes
Copilot reviewed 91 out of 94 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
vitest.config.mts |
Enforces global coverage thresholds and documents the rationale (ties to Section 3.6 in the architecture evaluation). |
turbo.json |
Adds a Turbo tasks pipeline for build, test, lint, type-check, and dev/storybook tasks with caching and dependency-aware execution. |
tsconfig.base.json |
Introduces a shared strict TypeScript base config (ES2020, bundler resolution, declarations, excludes tests/outputs) used across packages. |
tsconfig.react.json |
React-specific TS config extending the base with JSX and DOM libs. |
tsconfig.node.json |
Node-specific TS config extending the base without DOM libs. |
scripts/setup.sh |
New onboarding script that validates Node/pnpm, installs deps, builds core packages in order, runs tests, and prints next steps. |
scripts/README.md |
Documents setup.sh usage and clarifies the shadcn-related helper scripts and commands. |
packages/types/src/plugin-scope.ts |
Defines protocol-level types for plugin scopes (PluginScope, meta, inputs, event handlers, config) for use by core and plugin authors. |
packages/types/src/index.ts |
Re-exports the new plugin scope types (under PluginComponentMeta / PluginComponentInput aliases) from the types entrypoint. |
packages/plugin-view/src/index.tsx |
Namespaces object-view and view:simple registrations under plugin-view to avoid collisions in the central registry. |
packages/plugin-timeline/src/renderer.tsx |
Adds namespace: 'plugin-timeline' to the timeline plugin registration metadata. |
packages/plugin-markdown/src/index.tsx |
Adds namespace: 'plugin-markdown' to the markdown plugin registration metadata. |
packages/plugin-map/src/index.tsx |
Namespaces object-map as plugin-map and keeps plugin metadata intact. |
packages/plugin-kanban/src/index.tsx |
Namespaces the kanban component as plugin-kanban. |
packages/plugin-grid/src/index.tsx |
Namespaces object-grid as plugin-grid and changes the alias to a grid type in the view namespace (for { type: 'view:grid' } usage). |
packages/plugin-gantt/src/index.tsx |
Namespaces object-gantt as plugin-gantt. |
packages/plugin-form/src/index.tsx |
Namespaces object-form as plugin-form without affecting existing form handling. |
packages/plugin-editor/src/index.tsx |
Namespaces code-editor as plugin-editor. |
packages/plugin-dashboard/src/index.tsx |
Namespaces dashboard and metric widgets as plugin-dashboard. |
packages/plugin-chatbot/src/renderer.tsx |
Namespaces the chatbot component as plugin-chatbot. |
packages/plugin-charts/src/index.tsx |
Namespaces all chart components (bar-chart, chart, chart:bar) under plugin-charts. |
packages/plugin-calendar/src/index.tsx |
Namespaces object-calendar as plugin-calendar. |
packages/plugin-calendar/src/calendar-view-renderer.tsx |
Namespaces calendar-view as plugin-calendar. |
packages/plugin-aggrid/src/index.tsx |
Namespaces aggrid and object-aggrid registrations as plugin-aggrid. |
packages/fields/src/index.tsx |
Adds a lazy fieldWidgetMap, registerField/registerAllFields APIs (with React.lazy), and switches legacy registerFields registrations to use the 'field' namespace. |
packages/core/src/registry/__tests__/plugin-scope-integration.test.ts |
New integration tests for PluginSystem+PluginScope covering component isolation, state isolation and limits, event isolation/global events, lifecycle hooks, cleanup, and legacy (non-scoped) plugin behavior. |
packages/core/src/registry/Registry.ts |
Extends ComponentMeta with namespace, changes register to derive a namespace:type key, provides aliasing for bare type, and adds namespace-aware get, getConfig, has, and getNamespaceComponents. |
packages/core/src/registry/PluginSystem.ts |
Extends PluginDefinition to accept PluginScope, wires in PluginScopeImpl when useScope is true, tracks per-plugin scopes, cleans them up on unload, and adds getScope. |
packages/core/src/registry/PluginScopeImpl.ts |
Implements PluginScope with namespaced registration, per-plugin state (with size limits), scoped and global event buses, and cleanup logic. |
packages/core/src/index.ts |
Re-exports PluginScopeImpl from the core package alongside existing registry and plugin system exports. |
packages/components/src/renderers/overlay/tooltip.tsx |
Namespaces tooltip registration as ui. |
packages/components/src/renderers/overlay/sheet.tsx |
Namespaces sheet registration as ui. |
packages/components/src/renderers/overlay/popover.tsx |
Namespaces popover registration as ui. |
packages/components/src/renderers/overlay/menubar.tsx |
Namespaces menubar registration as ui. |
packages/components/src/renderers/overlay/hover-card.tsx |
Namespaces hover-card registration as ui. |
packages/components/src/renderers/overlay/dropdown-menu.tsx |
Namespaces dropdown-menu registration as ui. |
packages/components/src/renderers/overlay/drawer.tsx |
Namespaces drawer registration as ui. |
packages/components/src/renderers/overlay/dialog.tsx |
Namespaces dialog registration as ui. |
packages/components/src/renderers/overlay/context-menu.tsx |
Namespaces context-menu registration as ui. |
packages/components/src/renderers/overlay/alert-dialog.tsx |
Namespaces alert-dialog registration as ui. |
packages/components/src/renderers/navigation/sidebar.tsx |
Namespaces the sidebar-provider, sidebar, sidebar-group, sidebar-menu-button, and sidebar-trigger renderers as ui. |
packages/components/src/renderers/navigation/header-bar.tsx |
Namespaces header-bar registration as ui. |
packages/components/src/renderers/layout/tabs.tsx |
Namespaces tabs registration as ui. |
packages/components/src/renderers/layout/stack.tsx |
Namespaces stack layout renderer as ui. |
packages/components/src/renderers/layout/semantic.tsx |
Namespaces all semantic tag-based layout components as ui. |
packages/components/src/renderers/layout/page.tsx |
Namespaces page renderer as ui. |
packages/components/src/renderers/layout/grid.tsx |
Namespaces layout grid renderer as ui. |
packages/components/src/renderers/layout/flex.tsx |
Namespaces layout flex renderer as ui. |
packages/components/src/renderers/layout/container.tsx |
Namespaces container renderer as ui. |
packages/components/src/renderers/layout/card.tsx |
Namespaces card renderer as ui. |
packages/components/src/renderers/layout/aspect-ratio.tsx |
Namespaces aspect-ratio renderer as ui. |
packages/components/src/renderers/form/toggle.tsx |
Namespaces toggle renderer as ui. |
packages/components/src/renderers/form/textarea.tsx |
Namespaces textarea renderer as ui. |
packages/components/src/renderers/form/switch.tsx |
Namespaces switch renderer as ui. |
packages/components/src/renderers/form/slider.tsx |
Namespaces slider form renderer as ui. |
packages/components/src/renderers/form/select.tsx |
Namespaces select form renderer as ui. |
packages/components/src/renderers/form/radio-group.tsx |
Namespaces radio-group renderer as ui. |
packages/components/src/renderers/form/label.tsx |
Namespaces label renderer as ui. |
packages/components/src/renderers/form/input.tsx |
Namespaces the email and password input variants as ui (base input registration unchanged). |
packages/components/src/renderers/form/input-otp.tsx |
Namespaces input-otp as ui. |
packages/components/src/renderers/form/form.tsx |
Namespaces form renderer as ui. |
packages/components/src/renderers/form/file-upload.tsx |
Namespaces file-upload as ui. |
packages/components/src/renderers/form/date-picker.tsx |
Namespaces date-picker as ui. |
packages/components/src/renderers/form/command.tsx |
Namespaces command as ui. |
packages/components/src/renderers/form/combobox.tsx |
Namespaces combobox as ui. |
packages/components/src/renderers/form/checkbox.tsx |
Namespaces checkbox as ui. |
packages/components/src/renderers/form/calendar.tsx |
Namespaces form calendar as ui. |
packages/components/src/renderers/form/button.tsx |
Namespaces the form button renderer as ui. |
packages/components/src/renderers/feedback/toaster.tsx |
Namespaces toaster as ui. |
packages/components/src/renderers/feedback/toast.tsx |
Namespaces toast as ui. |
packages/components/src/renderers/feedback/spinner.tsx |
Namespaces spinner as ui. |
packages/components/src/renderers/feedback/sonner.tsx |
Namespaces sonner as ui. |
packages/components/src/renderers/feedback/skeleton.tsx |
Namespaces skeleton as ui. |
packages/components/src/renderers/feedback/progress.tsx |
Namespaces progress as ui. |
packages/components/src/renderers/feedback/loading.tsx |
Namespaces loading as ui. |
packages/components/src/renderers/feedback/empty.tsx |
Namespaces empty as ui. |
packages/components/src/renderers/disclosure/toggle-group.tsx |
Namespaces toggle-group as ui. |
packages/components/src/renderers/disclosure/collapsible.tsx |
Namespaces collapsible as ui. |
packages/components/src/renderers/disclosure/accordion.tsx |
Namespaces accordion as ui. |
packages/components/src/renderers/data-display/tree-view.tsx |
Namespaces tree-view as ui. |
packages/components/src/renderers/data-display/table.tsx |
Namespaces simple data-display table as ui. |
packages/components/src/renderers/data-display/statistic.tsx |
Removes a @ts-expect-error comment and namespaces statistic as ui. |
packages/components/src/renderers/data-display/list.tsx |
Namespaces list as ui. |
packages/components/src/renderers/data-display/kbd.tsx |
Namespaces kbd as ui. |
packages/components/src/renderers/data-display/breadcrumb.tsx |
Namespaces breadcrumb as ui. |
packages/components/src/renderers/data-display/badge.tsx |
Namespaces badge as ui. |
packages/components/src/renderers/data-display/avatar.tsx |
Namespaces avatar as ui. |
packages/components/src/renderers/data-display/alert.tsx |
Namespaces alert as ui. |
packages/components/src/renderers/complex/table.tsx |
Adds namespace: 'ui' metadata to the complex table renderer (keeping existing inputs/defaultProps). |
packages/components/src/renderers/complex/scroll-area.tsx |
Namespaces scroll-area as ui. |
packages/components/src/renderers/complex/resizable.tsx |
Namespaces resizable as ui. |
packages/components/src/renderers/complex/filter-builder.tsx |
Namespaces filter-builder as ui. |
packages/components/src/renderers/complex/data-table.tsx |
Namespaces data-table as ui. |
packages/components/src/renderers/complex/carousel.tsx |
Namespaces carousel as ui. |
packages/components/src/renderers/basic/text.tsx |
Namespaces basic text as ui. |
packages/components/src/renderers/basic/span.tsx |
Namespaces span as ui. |
packages/components/src/renderers/basic/separator.tsx |
Namespaces separator as ui. |
packages/components/src/renderers/basic/pagination.tsx |
Namespaces pagination as ui. |
packages/components/src/renderers/basic/navigation-menu.tsx |
Namespaces navigation-menu as ui. |
packages/components/src/renderers/basic/image.tsx |
Namespaces basic image as ui. |
packages/components/src/renderers/basic/icon.tsx |
Namespaces icon as ui. |
packages/components/src/renderers/basic/html.tsx |
Namespaces html content renderer as ui. |
packages/components/src/renderers/basic/div.tsx |
Namespaces basic div container as ui. |
packages/components/src/renderers/basic/button-group.tsx |
Namespaces button-group as ui. |
package.json |
Pins the repo’s package manager to pnpm@9.0.0, replaces script-based builds/tests/lint with Turbo (build, test, lint, type-check), and adds test:root for direct Vitest runs. |
README.md |
Adds a v0.4.0 "What’s New" section (namespaces, lazy fields, build perf), links to a Migration Guide, and documents the new setup script and architecture evaluation docs. |
EVALUATION_SUMMARY.md |
New high-level architecture evaluation summary with deliverables, metrics, roadmap, and immediate next steps for maintainers and contributors. |
ARCHITECTURE_EVALUATION.md |
Full detailed architecture evaluation (11 sections: overview, strengths, gaps, security, performance, scalability, DX, maintainability, i18n, a11y, and roadmap). |
.gitignore |
Ignores Turbo’s .turbo cache directory. |
.github/workflows/storybook-tests.yml |
Stops forcing pnpm v10 in Storybook test workflow, relying on the action’s default. |
.github/workflows/size-check.yml |
Removes explicit pnpm v10 pinning from the size-check workflow. |
.github/workflows/shadcn-check.yml |
Upgrades pnpm action to pnpm/action-setup@v4 without a hardcoded version. |
.github/workflows/release.yml |
Removes the pnpm v10 pin from the release workflow. |
.github/workflows/pr-checks.yml |
Removes the pnpm v10 pin from the PR checks workflow. |
.github/workflows/dependabot-auto-merge.yml |
Removes the pnpm v10 pin from the Dependabot auto-merge workflow. |
.github/workflows/ci.yml |
Removes pnpm v10 pinning from all CI matrix jobs while keeping pnpm setup. |
.github/workflows/changeset-release.yml |
Removes pnpm v10 pin from the Changesets-based release workflow. |
| const fieldWidgetMap: Record<string, () => Promise<{ default: React.ComponentType<any> }>> = { | ||
| // Basic fields | ||
| 'text': () => import('./widgets/TextField').then(m => ({ default: m.TextField })), | ||
| 'textarea': () => import('./widgets/TextAreaField').then(m => ({ default: m.TextAreaField })), | ||
| 'number': () => import('./widgets/NumberField').then(m => ({ default: m.NumberField })), | ||
| 'boolean': () => import('./widgets/BooleanField').then(m => ({ default: m.BooleanField })), | ||
| 'select': () => import('./widgets/SelectField').then(m => ({ default: m.SelectField })), | ||
| 'date': () => import('./widgets/DateField').then(m => ({ default: m.DateField })), | ||
| 'datetime': () => import('./widgets/DateTimeField').then(m => ({ default: m.DateTimeField })), | ||
| 'time': () => import('./widgets/TimeField').then(m => ({ default: m.TimeField })), | ||
|
|
||
| // Contact fields | ||
| 'email': () => import('./widgets/EmailField').then(m => ({ default: m.EmailField })), | ||
| 'phone': () => import('./widgets/PhoneField').then(m => ({ default: m.PhoneField })), | ||
| 'url': () => import('./widgets/UrlField').then(m => ({ default: m.UrlField })), | ||
|
|
||
| // Specialized fields | ||
| 'currency': () => import('./widgets/CurrencyField').then(m => ({ default: m.CurrencyField })), | ||
| 'percent': () => import('./widgets/PercentField').then(m => ({ default: m.PercentField })), | ||
| 'password': () => import('./widgets/PasswordField').then(m => ({ default: m.PasswordField })), | ||
| 'markdown': () => import('./widgets/RichTextField').then(m => ({ default: m.RichTextField })), | ||
| 'html': () => import('./widgets/RichTextField').then(m => ({ default: m.RichTextField })), | ||
| 'lookup': () => import('./widgets/LookupField').then(m => ({ default: m.LookupField })), | ||
| 'master_detail': () => import('./widgets/MasterDetailField').then(m => ({ default: m.MasterDetailField })), | ||
|
|
||
| // File fields | ||
| 'file': () => import('./widgets/FileField').then(m => ({ default: m.FileField })), | ||
| 'image': () => import('./widgets/ImageField').then(m => ({ default: m.ImageField })), | ||
|
|
||
| // Location field | ||
| 'location': () => import('./widgets/LocationField').then(m => ({ default: m.LocationField })), | ||
|
|
||
| // Computed/Read-only fields | ||
| 'formula': () => import('./widgets/FormulaField').then(m => ({ default: m.FormulaField })), | ||
| 'summary': () => import('./widgets/SummaryField').then(m => ({ default: m.SummaryField })), | ||
| 'auto_number': () => import('./widgets/AutoNumberField').then(m => ({ default: m.AutoNumberField })), | ||
|
|
||
| // User fields | ||
| 'user': () => import('./widgets/UserField').then(m => ({ default: m.UserField })), | ||
| 'owner': () => import('./widgets/UserField').then(m => ({ default: m.UserField })), | ||
|
|
||
| // Complex data types | ||
| 'object': () => import('./widgets/ObjectField').then(m => ({ default: m.ObjectField })), | ||
| 'vector': () => import('./widgets/VectorField').then(m => ({ default: m.VectorField })), | ||
| 'grid': () => import('./widgets/GridField').then(m => ({ default: m.GridField })), | ||
|
|
||
| // Additional field types from @objectstack/spec | ||
| 'color': () => import('./widgets/ColorField').then(m => ({ default: m.ColorField })), | ||
| 'slider': () => import('./widgets/SliderField').then(m => ({ default: m.SliderField })), | ||
| 'rating': () => import('./widgets/RatingField').then(m => ({ default: m.RatingField })), | ||
| 'code': () => import('./widgets/CodeField').then(m => ({ default: m.CodeField })), | ||
| 'avatar': () => import('./widgets/AvatarField').then(m => ({ default: m.AvatarField })), | ||
| 'address': () => import('./widgets/AddressField').then(m => ({ default: m.AddressField })), | ||
| 'geolocation': () => import('./widgets/GeolocationField').then(m => ({ default: m.GeolocationField })), | ||
| 'signature': () => import('./widgets/SignatureField').then(m => ({ default: m.SignatureField })), | ||
| 'qrcode': () => import('./widgets/QRCodeField').then(m => ({ default: m.QRCodeField })), | ||
| }; | ||
|
|
||
| /** | ||
| * Register a specific field type lazily | ||
| * @param fieldType - The field type to register (e.g., 'text', 'number') | ||
| * | ||
| * @example | ||
| * // Register only the text field | ||
| * registerField('text'); | ||
| */ | ||
| export function registerField(fieldType: string): void { | ||
| const loader = fieldWidgetMap[fieldType]; | ||
| if (!loader) { | ||
| console.warn(`Unknown field type: ${fieldType}`); | ||
| return; | ||
| } | ||
|
|
||
| // Create lazy component | ||
| const LazyFieldWidget = React.lazy(loader); | ||
|
|
||
| // Register with field namespace | ||
| const renderer = createFieldRenderer(LazyFieldWidget); | ||
| ComponentRegistry.register(fieldType, renderer, { namespace: 'field' }); | ||
| } | ||
|
|
||
| /** | ||
| * Register all field types (for backward compatibility) | ||
| * This function auto-registers all field widgets on import. | ||
| * | ||
| * For better tree-shaking, use registerField() to register only needed fields. | ||
| * | ||
| * @example | ||
| * // Register all fields at once | ||
| * registerAllFields(); | ||
| */ | ||
| export function registerAllFields(): void { | ||
| Object.keys(fieldWidgetMap).forEach(fieldType => { | ||
| registerField(fieldType); | ||
| }); | ||
| } |
There was a problem hiding this comment.
The new lazy field registration path (registerField / registerAllFields) wraps widgets with React.lazy, but the resulting FieldRenderer is rendered via SchemaRenderer without any Suspense boundary in the fields/package path. If a consumer follows the README’s new "Lazy Field Registration" example and uses registerField('text') (instead of the legacy registerFields()), they will render a lazy component without a surrounding Suspense, which will cause React to throw at runtime. To make this safe, either ensure that all field renderers are ultimately wrapped in a Suspense boundary (e.g., at the SchemaRenderer level or in the form/layout renderers), or switch the lazy-loading API to a pattern similar to createLazyPlugin that provides its own Suspense wrapper around the lazy widget.
| /** | ||
| * Register a specific field type lazily | ||
| * @param fieldType - The field type to register (e.g., 'text', 'number') | ||
| * | ||
| * @example | ||
| * // Register only the text field | ||
| * registerField('text'); | ||
| */ | ||
| export function registerField(fieldType: string): void { | ||
| const loader = fieldWidgetMap[fieldType]; | ||
| if (!loader) { | ||
| console.warn(`Unknown field type: ${fieldType}`); | ||
| return; | ||
| } | ||
|
|
||
| // Create lazy component | ||
| const LazyFieldWidget = React.lazy(loader); | ||
|
|
||
| // Register with field namespace | ||
| const renderer = createFieldRenderer(LazyFieldWidget); | ||
| ComponentRegistry.register(fieldType, renderer, { namespace: 'field' }); | ||
| } | ||
|
|
||
| /** | ||
| * Register all field types (for backward compatibility) | ||
| * This function auto-registers all field widgets on import. | ||
| * | ||
| * For better tree-shaking, use registerField() to register only needed fields. | ||
| * | ||
| * @example | ||
| * // Register all fields at once | ||
| * registerAllFields(); | ||
| */ | ||
| export function registerAllFields(): void { | ||
| Object.keys(fieldWidgetMap).forEach(fieldType => { | ||
| registerField(fieldType); | ||
| }); | ||
| } |
There was a problem hiding this comment.
The new lazy field registration APIs (registerField and registerAllFields) introduce important behavior in this module, but there are no tests covering them (e.g., verifying that a field type registers correctly, renders through SchemaRenderer, and handles unknown field types as expected). Given that this package already has Vitest coverage for field widgets, adding tests for these functions would help keep coverage above the new global thresholds and prevent regressions in the lazy field loading behavior.
| [**Documentation**](https://www.objectui.org) | [**Quick Start**](#quick-start) | [**Migration Guide**](./MIGRATION_GUIDE.md) | ||
|
|
||
| </div> | ||
|
|
||
| --- | ||
|
|
||
| ## 🎉 What's New in v0.4.0 | ||
|
|
||
| ### Component Namespaces | ||
| Organize components into namespaces to prevent naming conflicts: | ||
| ```typescript | ||
| ComponentRegistry.register('button', ButtonComponent, { | ||
| namespace: 'ui' // ✨ New: Prevent conflicts across plugins | ||
| }); | ||
| ``` | ||
|
|
||
| ### Lazy Field Registration | ||
| Load only the fields you need for 30-50% smaller bundles: | ||
| ```typescript | ||
| import { registerField } from '@object-ui/fields'; | ||
|
|
||
| // Only load what you use | ||
| registerField('text'); | ||
| registerField('number'); | ||
| // 70% smaller bundle size! 🚀 | ||
| ``` | ||
|
|
||
| ### Build Performance | ||
| - **3-5x faster CI builds** with Turbo v2 | ||
| - **Parallel package builds** with intelligent caching | ||
| - **Instant rebuilds** for unchanged packages | ||
|
|
||
| 👉 **[Read the Migration Guide](./MIGRATION_GUIDE.md)** for details and examples. |
There was a problem hiding this comment.
The README now links to ./MIGRATION_GUIDE.md and describes a "Migration Guide" in the v0.4.0 section, but that file is not present in the repository root, so the link will 404 for contributors and users. Either add the missing MIGRATION_GUIDE.md file (as suggested in EVALUATION_SUMMARY.md) or update this link and text to point to the correct migration documentation location.
| register(type: string, component: ComponentRenderer<T>, meta?: ComponentMeta) { | ||
| // Construct the full type with namespace if provided | ||
| const namespace = meta?.namespace; | ||
| const fullType = namespace ? `${namespace}:${type}` : type; | ||
| const fullType = meta?.namespace ? `${meta.namespace}:${type}` : type; | ||
|
|
||
| // Warn if overwriting an existing registration | ||
| if (this.components.has(fullType)) { | ||
| // console.warn(`Component type "${fullType}" is already registered. Overwriting.`); | ||
| } | ||
|
|
||
| // Deprecation warning for non-namespaced registrations | ||
| // (only for new registrations, not for backward compatibility lookups) | ||
| if (!namespace && typeof console !== 'undefined' && console.warn) { | ||
| console.warn( | ||
| `[ObjectUI] Registering component "${type}" without a namespace is deprecated. ` + | ||
| `Please provide a namespace via the meta.namespace option. ` + | ||
| `Example: ComponentRegistry.register('${type}', component, { namespace: 'ui' })` | ||
| ); | ||
| } | ||
|
|
||
| const config: ComponentConfig<T> = { | ||
| this.components.set(fullType, { | ||
| type: fullType, | ||
| component, | ||
| ...meta | ||
| }; | ||
| }); | ||
|
|
||
| // Store under the full namespaced type | ||
| this.components.set(fullType, config); | ||
|
|
||
| // For backward compatibility, also store under the non-namespaced type | ||
| // if a namespace was provided (so 'button' works even when registered as 'ui:button') | ||
| if (namespace) { | ||
| this.components.set(type, config); | ||
| // Also register without namespace for backward compatibility | ||
| // This allows "button" to work even when registered as "ui:button" | ||
| if (meta?.namespace && !this.components.has(type)) { | ||
| this.components.set(type, { | ||
| type: fullType, // Keep reference to namespaced type | ||
| component, | ||
| ...meta | ||
| }); | ||
| } |
There was a problem hiding this comment.
The new namespace-aware aliasing logic in register introduces inconsistent behavior when the same type is registered multiple times with a namespace. Because the alias entry for the bare type is only created when !this.components.has(type), subsequent registrations for the same type and any namespace will update the namespaced key (e.g. "ui:table") but will not update the fallback alias at "table", leaving get('table') returning a stale component while get('ui:table') returns the latest one. This is observable today with duplicate table registrations in packages/components/src/renderers/data-display/table.tsx and packages/components/src/renderers/complex/table.tsx, where the alias and namespaced lookups can diverge. Consider either (a) updating the alias entry on every registration, (b) not creating an un-namespaced alias when a namespaced registration for that type already exists, or (c) disallowing duplicate registrations for the same fully-qualified namespace:type and surfacing an explicit error/warning instead of silently diverging.
| name: 'plugin-b', | ||
| version: '1.0.0', | ||
| register: (scope: PluginScope) => { | ||
| const [config, setConfig] = scope.useState('config', { theme: 'auto' }); |
There was a problem hiding this comment.
Unused variable setConfig.
Comprehensive architecture assessment identifying structural improvements and implementing immediate build performance gains.
Architecture Analysis
Deliverables:
ARCHITECTURE_EVALUATION.md- 11-aspect analysis covering layering, TypeScript usage, plugin system, security, performance, scalabilityARCHITECTURE_EVALUATION.zh-CN.md- Chinese translationKey findings:
Prioritized improvements:
ui:button,plugin-grid:table) - prevents conflictsBuild System
Configured Turbo for parallel builds with dependency-aware caching:
{ "pipeline": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] } } }Impact:
TypeScript Consolidation
Replaced 24 per-package tsconfigs with shared base:
Packages now
extends: "../../tsconfig.react.json"- single source of truth for compiler flags.Developer Onboarding
Automated setup script (
scripts/setup.sh):Metrics
Next Steps
Phase 1 (Q2 2026):
ComponentRegistry.register(type, component, { namespace }))Phase 2-3: Plugin isolation, i18n, a11y automation, plugin marketplace
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.