Skip to content

Commit bd12758

Browse files
committed
Update files
1 parent c3ecde0 commit bd12758

8 files changed

Lines changed: 210 additions & 49 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
- **`@object-ui/plugin-list` & `@object-ui/plugin-detail`: shared `ComponentRegistry` singleton broken when consumed via published dist.** Both packages' `vite.config.ts` only listed `react`, `react-dom`, `react/jsx-runtime` in `rollupOptions.external`. As a result Rolldown inlined `@object-ui/core` (and a large chunk of lucide-react) into each plugin's bundle, giving every plugin its **own private `ComponentRegistry` instance**. Downstream apps that loaded the published plugins from npm saw `registry.get('object-grid')` from one plugin return `undefined` for components registered by another plugin — only workspace `alias` overrides pointing back to `packages/*/src` masked the issue. The `external` config now uses a regex covering all `@object-ui/*` workspace packages, the `@object-ui/*` deps have been moved from `dependencies` to `peerDependencies` (versioned `workspace:^` for publish-time rewrite, mirrored in `devDependencies` for local builds), and a regression test (`packages/core/src/registry/__tests__/cross-plugin-singleton.test.ts`) imports the built dist of `@object-ui/plugin-grid` and `@object-ui/plugin-list` and asserts cross-plugin lookup works against the same `ComponentRegistry` singleton. Downstream consumers (e.g. framework `apps/dashboard`) can now drop any `OBJECTUI_ROOT` / `packages/*/src` aliases.
13+
- `packages/plugin-list/dist/index.js` shrank from a multi-megabyte icon bundle to **67 kB** (gzip 16 kB).
14+
- `packages/plugin-detail/dist/index.js` shrank to **124 kB** (gzip 28 kB).
1215
- **CI: `apps/console` ObjectView test failures.** The `vi.mock('@object-ui/i18n', ...)`
1316
block in `apps/console/dev/__tests__/ObjectView.test.tsx` was returning bare
1417
functions for `useObjectTranslation` / `useObjectLabel`, but the real hooks
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* ObjectUI
3+
* Copyright (c) 2024-present ObjectStack Inc.
4+
*
5+
* Smoke / regression test: every published `@object-ui/plugin-*` MUST share
6+
* the single `ComponentRegistry` singleton exported from `@object-ui/core`.
7+
*
8+
* If a plugin's bundle accidentally inlines `@object-ui/core`, it ends up with
9+
* its own private `ComponentRegistry` — components registered by that plugin
10+
* become invisible to other plugins, and downstream apps see `undefined`
11+
* lookups across plugin boundaries.
12+
*
13+
* This test imports the **built dist** of two representative plugins via
14+
* explicit relative paths (bypassing vitest's source aliases) and asserts
15+
* that the components they register are visible on the same singleton.
16+
*/
17+
18+
import path from 'node:path';
19+
import { existsSync } from 'node:fs';
20+
import { pathToFileURL } from 'node:url';
21+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
22+
23+
import { ComponentRegistry } from '../Registry.js';
24+
25+
const PLUGIN_GRID_DIST = path.resolve(
26+
__dirname,
27+
'../../../../plugin-grid/dist/index.js',
28+
);
29+
const PLUGIN_LIST_DIST = path.resolve(
30+
__dirname,
31+
'../../../../plugin-list/dist/index.js',
32+
);
33+
34+
const distAvailable =
35+
existsSync(PLUGIN_GRID_DIST) && existsSync(PLUGIN_LIST_DIST);
36+
37+
describe.skipIf(!distAvailable)(
38+
'ComponentRegistry singleton across plugin dist bundles',
39+
() => {
40+
const baselineSize = ComponentRegistry.getAllConfigs().length;
41+
42+
beforeAll(async () => {
43+
// Import via file URL so Node treats them as ESM modules and the
44+
// workspace aliases (which only apply to bare specifiers) are bypassed.
45+
await import(pathToFileURL(PLUGIN_GRID_DIST).href);
46+
await import(pathToFileURL(PLUGIN_LIST_DIST).href);
47+
});
48+
49+
afterAll(() => {
50+
// Best-effort cleanup: drop registry entries the plugins added so this
51+
// test doesn't pollute other tests in the same vitest worker.
52+
const components = (ComponentRegistry as unknown as {
53+
components: Map<string, unknown>;
54+
}).components;
55+
for (const key of [
56+
'plugin-grid:object-grid',
57+
'object-grid',
58+
'view:grid',
59+
'plugin-grid:import-wizard',
60+
'import-wizard',
61+
'plugin-list:list-view',
62+
'list-view',
63+
'view:list',
64+
]) {
65+
components.delete(key);
66+
}
67+
});
68+
69+
it('plugin-grid registers object-grid into the shared singleton', () => {
70+
const cfg =
71+
ComponentRegistry.getConfig('plugin-grid:object-grid') ??
72+
ComponentRegistry.getConfig('object-grid');
73+
expect(cfg).toBeDefined();
74+
expect(cfg?.component).toBeTruthy();
75+
});
76+
77+
it('plugin-list registers list-view into the shared singleton', () => {
78+
const cfg =
79+
ComponentRegistry.getConfig('plugin-list:list-view') ??
80+
ComponentRegistry.getConfig('list-view');
81+
expect(cfg).toBeDefined();
82+
expect(cfg?.component).toBeTruthy();
83+
});
84+
85+
it('plugin-list can resolve a component registered by plugin-grid', () => {
86+
// Cross-plugin lookup — the actual bug scenario reported downstream.
87+
const fromList =
88+
ComponentRegistry.get('plugin-grid:object-grid') ??
89+
ComponentRegistry.get('object-grid');
90+
expect(fromList).toBeDefined();
91+
});
92+
93+
it('importing both plugins increases registry size on the singleton', () => {
94+
expect(ComponentRegistry.getAllConfigs().length).toBeGreaterThan(
95+
baselineSize,
96+
);
97+
});
98+
},
99+
);

packages/plugin-detail/package.json

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@object-ui/plugin-detail",
3-
"version": "4.0.1",
3+
"version": "4.0.2",
44
"type": "module",
55
"license": "MIT",
66
"description": "DetailView plugin for Object UI - comprehensive detail page with sections, tabs, and related lists",
@@ -31,18 +31,23 @@
3131
"lint": "eslint ."
3232
},
3333
"dependencies": {
34-
"@object-ui/components": "workspace:*",
35-
"@object-ui/core": "workspace:*",
36-
"@object-ui/fields": "workspace:*",
37-
"@object-ui/react": "workspace:*",
38-
"@object-ui/types": "workspace:*",
3934
"lucide-react": "^1.14.0"
4035
},
4136
"peerDependencies": {
37+
"@object-ui/components": "workspace:^",
38+
"@object-ui/core": "workspace:^",
39+
"@object-ui/fields": "workspace:^",
40+
"@object-ui/react": "workspace:^",
41+
"@object-ui/types": "workspace:^",
4242
"react": "^18.0.0 || ^19.0.0",
4343
"react-dom": "^18.0.0 || ^19.0.0"
4444
},
4545
"devDependencies": {
46+
"@object-ui/components": "workspace:*",
47+
"@object-ui/core": "workspace:*",
48+
"@object-ui/fields": "workspace:*",
49+
"@object-ui/react": "workspace:*",
50+
"@object-ui/types": "workspace:*",
4651
"@types/react": "19.2.14",
4752
"@types/react-dom": "19.2.3",
4853
"@vitejs/plugin-react": "^6.0.1",

packages/plugin-detail/vite.config.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,23 @@ export default defineConfig({
3333
fileName: (format) => `index.${format === 'es' ? 'js' : 'umd.cjs'}`,
3434
},
3535
rollupOptions: {
36-
external: ['react', 'react-dom', 'react/jsx-runtime'],
36+
// IMPORTANT: do NOT inline @object-ui/* runtime packages.
37+
// Each plugin must share the same ComponentRegistry singleton from
38+
// @object-ui/core; bundling core into every plugin creates per-plugin
39+
// private registries and breaks cross-plugin component lookup.
40+
external: (id) =>
41+
/^(react|react-dom|react\/jsx-runtime|@object-ui\/(core|types|react|components|fields|i18n|mobile|layout|app-shell|theme|providers)|lucide-react)(\/|$)/.test(id),
3742
output: {
3843
globals: {
3944
react: 'React',
4045
'react-dom': 'ReactDOM',
4146
'react/jsx-runtime': 'jsxRuntime',
47+
'@object-ui/core': 'ObjectUICore',
48+
'@object-ui/types': 'ObjectUITypes',
49+
'@object-ui/react': 'ObjectUIReact',
50+
'@object-ui/components': 'ObjectUIComponents',
51+
'@object-ui/fields': 'ObjectUIFields',
52+
'lucide-react': 'LucideReact',
4253
},
4354
},
4455
},

packages/plugin-list/package.json

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@object-ui/plugin-list",
3-
"version": "4.0.1",
3+
"version": "4.0.2",
44
"type": "module",
55
"license": "MIT",
66
"description": "ListView plugin for Object UI - unified view component with view type switching",
@@ -31,19 +31,25 @@
3131
"lint": "eslint ."
3232
},
3333
"dependencies": {
34-
"@object-ui/components": "workspace:*",
35-
"@object-ui/core": "workspace:*",
36-
"@object-ui/i18n": "workspace:*",
37-
"@object-ui/mobile": "workspace:*",
38-
"@object-ui/react": "workspace:*",
39-
"@object-ui/types": "workspace:*",
4034
"lucide-react": "^1.14.0"
4135
},
4236
"peerDependencies": {
37+
"@object-ui/components": "workspace:^",
38+
"@object-ui/core": "workspace:^",
39+
"@object-ui/i18n": "workspace:^",
40+
"@object-ui/mobile": "workspace:^",
41+
"@object-ui/react": "workspace:^",
42+
"@object-ui/types": "workspace:^",
4343
"react": "^18.0.0 || ^19.0.0",
4444
"react-dom": "^18.0.0 || ^19.0.0"
4545
},
4646
"devDependencies": {
47+
"@object-ui/components": "workspace:*",
48+
"@object-ui/core": "workspace:*",
49+
"@object-ui/i18n": "workspace:*",
50+
"@object-ui/mobile": "workspace:*",
51+
"@object-ui/react": "workspace:*",
52+
"@object-ui/types": "workspace:*",
4753
"@types/react": "19.2.14",
4854
"@types/react-dom": "19.2.3",
4955
"@vitejs/plugin-react": "^6.0.1",

packages/plugin-list/vite.config.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,25 @@ export default defineConfig({
3333
fileName: (format) => `index.${format === 'es' ? 'js' : 'umd.cjs'}`,
3434
},
3535
rollupOptions: {
36-
external: ['react', 'react-dom', 'react/jsx-runtime'],
36+
// IMPORTANT: do NOT inline @object-ui/* runtime packages.
37+
// Each plugin must share the same ComponentRegistry singleton from
38+
// @object-ui/core; bundling core into every plugin creates per-plugin
39+
// private registries and breaks cross-plugin component lookup.
40+
external: (id) =>
41+
/^(react|react-dom|react\/jsx-runtime|@object-ui\/(core|types|react|components|fields|i18n|mobile|layout|app-shell|theme|providers)|lucide-react)(\/|$)/.test(id),
3742
output: {
3843
globals: {
3944
react: 'React',
4045
'react-dom': 'ReactDOM',
4146
'react/jsx-runtime': 'jsxRuntime',
47+
'@object-ui/core': 'ObjectUICore',
48+
'@object-ui/types': 'ObjectUITypes',
49+
'@object-ui/react': 'ObjectUIReact',
50+
'@object-ui/components': 'ObjectUIComponents',
51+
'@object-ui/fields': 'ObjectUIFields',
52+
'@object-ui/i18n': 'ObjectUIi18n',
53+
'@object-ui/mobile': 'ObjectUIMobile',
54+
'lucide-react': 'LucideReact',
4255
},
4356
},
4457
},

0 commit comments

Comments
 (0)