Skip to content

Commit 724c3c9

Browse files
ged-odoomcm-odoo
authored andcommitted
[imp] simplify plugin and resources system
- remove hardcoded resource in plugin - add useResource
1 parent ae9b214 commit 724c3c9

File tree

7 files changed

+175
-119
lines changed

7 files changed

+175
-119
lines changed

src/runtime/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
comment,
1414
} from "./blockdom";
1515
import { mainEventHandler } from "./event_handling";
16+
export { Resource, useResource } from "./resource";
1617
export { Registry } from "./registry";
1718

1819
config.shouldNormalizeDom = false;

src/runtime/lifecycle_hooks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export function onWillUnmount(fn: () => void | any) {
5050
export function onWillDestroy(fn: () => void | any) {
5151
const pm = _getCurrentPluginManager();
5252
if (pm) {
53-
pm.onDestroyCb.push(fn);
53+
(pm as any).onDestroyCb.push(fn);
5454
} else {
5555
const node = getCurrent();
5656
node.willDestroy.push(decorate(node, fn, "onWillDestroy"));

src/runtime/plugins.ts

Lines changed: 7 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { OwlError } from "../common/owl_error";
22
import { getCurrent } from "./component_node";
3-
import { derived } from "./reactivity/derived";
4-
import { proxy } from "./reactivity/proxy";
53

64
let currentPluginManager: PluginManager | null = null;
75

@@ -10,67 +8,43 @@ export const _getCurrentPluginManager = () => currentPluginManager;
108
export interface PluginConstructor {
119
new (): Plugin;
1210
id: string;
13-
resources: Record<string, any>;
1411
}
1512

1613
export class Plugin {
1714
static id: string = "";
1815

19-
// can define the type of resources, and some information, such as, is the
20-
// resource global or not
21-
static resources = {};
22-
23-
resources: Record<string, any> = {};
24-
2516
setup() {}
2617
}
2718

2819
export class PluginManager {
2920
private children: PluginManager[] = [];
3021
private parent: PluginManager | null;
3122
private plugins: Record<string, Plugin>;
32-
private resources: Record<string, any>;
33-
34-
onDestroyCb: Function[] = [];
23+
private onDestroyCb: Function[] = [];
3524

3625
constructor(parent: PluginManager | null) {
3726
this.parent = parent;
3827
this.parent?.children.push(this);
3928
this.plugins = this.parent ? Object.create(this.parent.plugins) : {};
40-
this.resources = this.parent ? Object.create(this.parent.resources) : {};
4129
}
4230

4331
destroy() {
4432
for (let children of this.children) {
4533
children.destroy();
4634
}
4735

48-
const plugins: Plugin[] = [];
49-
for (let id in this.plugins) {
50-
if (this.plugins.hasOwnProperty(id)) {
51-
const plugin = this.plugins[id];
52-
// resources
53-
for (let r in plugin.resources) {
54-
delete this.resources[r].sources[id];
55-
}
56-
57-
plugins.push(this.plugins[id]);
58-
delete this.plugins[id];
59-
}
60-
}
61-
6236
const cbs = this.onDestroyCb;
6337
while (cbs.length) {
6438
cbs.pop()!();
6539
}
6640
}
6741

68-
getPlugin<T extends Plugin>(name: string): T | null {
69-
return (this.plugins[name] as T) || null;
42+
getPluginById<T extends Plugin>(id: string): T | null {
43+
return (this.plugins[id] as T) || null;
7044
}
7145

72-
getResource(name: string): any[] {
73-
return this.resources[name].fn();
46+
getPlugin<T extends PluginConstructor>(pluginType: T): InstanceType<T> | null {
47+
return this.getPluginById<InstanceType<T>>(pluginType.id);
7448
}
7549

7650
startPlugins(pluginTypes: PluginConstructor[]): Plugin[] {
@@ -88,43 +62,11 @@ export class PluginManager {
8862
continue;
8963
}
9064

91-
for (let r in pluginType.resources) {
92-
const sources: { [key: string]: Plugin } = proxy({});
93-
const fn = derived(() => {
94-
const result = [];
95-
for (let name in sources) {
96-
const plugin = sources[name];
97-
const value = plugin.resources[r];
98-
if (Array.isArray(value)) {
99-
result.push(...value);
100-
} else {
101-
result.push(value);
102-
}
103-
}
104-
return result;
105-
});
106-
this.resources[r] = { sources, fn };
107-
}
108-
10965
const plugin = new pluginType();
11066
this.plugins[pluginType.id] = plugin;
11167
plugins.push(plugin);
11268
}
11369

114-
// aggregate resources
115-
for (let name in this.plugins) {
116-
const p = this.plugins[name];
117-
for (let r in p.resources) {
118-
this.resources[r].sources[name] = p;
119-
// const value = p.resources[r];
120-
// if (Array.isArray(value)) {
121-
// this.resources[r].push(...value);
122-
// } else {
123-
// this.resources[r].push(value);
124-
// }
125-
}
126-
}
127-
12870
// setup phase
12971
for (let p of plugins) {
13072
p.setup();
@@ -139,11 +81,11 @@ export function plugin<T extends PluginConstructor>(pluginType: T): InstanceType
13981
// getCurrent will throw if we're not in a component
14082
const manager = currentPluginManager || getCurrent().pluginManager;
14183

142-
let plugin = manager.getPlugin<InstanceType<T>>(pluginType.id);
84+
let plugin = manager.getPluginById<InstanceType<T>>(pluginType.id);
14385
if (!plugin) {
14486
if (manager === currentPluginManager) {
14587
manager.startPlugins([pluginType]);
146-
plugin = manager.getPlugin<InstanceType<T>>(pluginType.id)!;
88+
plugin = manager.getPluginById<InstanceType<T>>(pluginType.id)!;
14789
} else {
14890
throw new OwlError(`Unknown plugin "${pluginType.id}"`);
14991
}

src/runtime/resource.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
import { onWillDestroy } from "./lifecycle_hooks";
12
import { derived } from "./reactivity/derived";
23
import { signal, Signal } from "./reactivity/signal";
34
import { TypeDescription, validateType } from "./validation";
45

56
export class Resource<T> {
6-
_items: Signal<[number, T][]> = signal([]);
7-
_name: string;
8-
_type?: TypeDescription;
7+
private _items: Signal<[number, T][]> = signal([]);
8+
private _name: string;
9+
private _type?: TypeDescription;
910

1011
constructor(name?: string, type?: TypeDescription) {
11-
this._name = name || "registry";
12+
this._name = name || "resource";
1213
this._type = type;
1314
}
1415

@@ -22,7 +23,7 @@ export class Resource<T> {
2223
if (this._type) {
2324
const error = validateType("item", item as any, this._type as any);
2425
if (error) {
25-
throw new Error("Invalid type: " + error);
26+
throw new Error(`Invalid type: ${error} (resource '${this._name}')`);
2627
}
2728
}
2829
this._items().push([sequence, item]);
@@ -36,3 +37,14 @@ export class Resource<T> {
3637
return this;
3738
}
3839
}
40+
41+
export function useResource<T>(r: Resource<T>, elements: T[]) {
42+
for (let elem of elements) {
43+
r.add(elem);
44+
}
45+
onWillDestroy(() => {
46+
for (let elem of elements) {
47+
r.remove(elem);
48+
}
49+
});
50+
}

tests/components/__snapshots__/plugins.test.ts.snap

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,35 @@ exports[`basic use 1`] = `
2424
}"
2525
`;
2626

27+
exports[`components can register resources 1`] = `
28+
"function anonymous(app, bdom, helpers
29+
) {
30+
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
31+
const comp1 = app.createComponent(\`Level2\`, true, false, false, []);
32+
33+
return function template(ctx, node, key = "") {
34+
const b2 = text(\`1 | \`);
35+
const b3 = comp1({}, key + \`__1\`, node, this, null);
36+
return multi([b2, b3]);
37+
}
38+
}"
39+
`;
40+
41+
exports[`components can register resources 2`] = `
42+
"function anonymous(app, bdom, helpers
43+
) {
44+
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
45+
let { safeOutput } = helpers;
46+
47+
return function template(ctx, node, key = "") {
48+
const b2 = text(\`2: \`);
49+
const b3 = safeOutput(ctx['this'].a.value());
50+
const b4 = text(\` \`);
51+
return multi([b2, b3, b4]);
52+
}
53+
}"
54+
`;
55+
2756
exports[`components can start plugins 1`] = `
2857
"function anonymous(app, bdom, helpers
2958
) {

tests/components/plugins.test.ts

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
import { Component, mount, plugin, Plugin, PluginManager, usePlugins, xml } from "../../src";
1+
import {
2+
Component,
3+
derived,
4+
mount,
5+
plugin,
6+
Plugin,
7+
PluginManager,
8+
usePlugins,
9+
xml,
10+
} from "../../src";
11+
import { Resource, useResource } from "../../src/runtime/resource";
212
import { makeTestFixture, snapshotEverything } from "../helpers";
313

414
let fixture: HTMLElement;
@@ -148,8 +158,8 @@ test("components start plugins at their level", async () => {
148158
const pluginManager = new PluginManager(null);
149159

150160
await mount(Level1, fixture, { pluginManager });
151-
expect(pluginManager.getPlugin("a")).toBe(null);
152-
expect(pluginManager.getPlugin("b")).toBe(null);
161+
expect(pluginManager.getPluginById("a")).toBe(null);
162+
expect(pluginManager.getPluginById("b")).toBe(null);
153163
expect(fixture.innerHTML).toBe("1 | 2: pA | 3: pA - pB");
154164
});
155165

@@ -191,3 +201,44 @@ test("shadow plugin", async () => {
191201
await mount(Level1, fixture, { pluginManager });
192202
expect(fixture.innerHTML).toBe("a | shadow");
193203
});
204+
205+
test("components can register resources", async () => {
206+
class PluginA extends Plugin {
207+
static id = "a";
208+
colors = new Resource<string>("colors", String);
209+
210+
value = derived(() => {
211+
return this.colors.items().join("|");
212+
});
213+
}
214+
215+
class Level2 extends Component {
216+
static template = xml`2: <t t-out="this.a.value()"/> `;
217+
218+
a = plugin(PluginA);
219+
setup() {
220+
useResource(this.a.colors, ["from lvl 2"]);
221+
expect(this.a.colors.items()).toEqual(["from lvl 1", "from lvl 2"]);
222+
}
223+
}
224+
225+
class Level1 extends Component {
226+
static template = xml`1 | <Level2/>`;
227+
static components = { Level2 };
228+
229+
setup() {
230+
const a = plugin(PluginA);
231+
useResource(a.colors, ["from lvl 1"]);
232+
expect(a.colors.items()).toEqual(["from lvl 1"]);
233+
}
234+
}
235+
236+
const pluginManager = new PluginManager(null);
237+
pluginManager.startPlugins([PluginA]);
238+
const a = pluginManager.getPlugin(PluginA)!;
239+
expect(a.colors.items()).toEqual([]);
240+
241+
await mount(Level1, fixture, { pluginManager });
242+
expect(a.colors.items()).toEqual(["from lvl 1", "from lvl 2"]);
243+
expect(fixture.innerHTML).toBe("1 | 2: from lvl 1|from lvl 2 ");
244+
});

0 commit comments

Comments
 (0)