Skip to content

Commit db63b65

Browse files
committed
split enableLegacyHooks
1 parent 08c8b40 commit db63b65

1 file changed

Lines changed: 102 additions & 56 deletions

File tree

packages/feathers/src/hooks/legacy.ts

Lines changed: 102 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -66,95 +66,141 @@ export function collectLegacyHooks (target: any, method: string) {
6666

6767
// Converts different hook registration formats into the
6868
// same internal format
69-
export function convertHookData (obj: any) {
70-
let hook: any = {};
69+
export function convertHookData (input: any) {
70+
const result: { [ method: string ]: LegacyHookFunction[] } = {};
7171

72-
if (Array.isArray(obj)) {
73-
hook = { all: obj };
74-
} else if (typeof obj !== 'object') {
75-
hook = { all: [ obj ] };
72+
if (Array.isArray(input)) {
73+
result.all = input;
74+
} else if (typeof input !== 'object') {
75+
result.all = [ input ];
7676
} else {
77-
for (const [key, value] of Object.entries(obj)) {
78-
hook[key] = !Array.isArray(value) ? [ value ] : value;
77+
for (const key of Object.keys(input)) {
78+
const value = input[key];
79+
result[key] = Array.isArray(value) ? value : [ value ];
7980
}
8081
}
8182

82-
return hook;
83+
return result;
8384
}
8485

85-
const types = ['before', 'after', 'error'];
86+
type LegacyType = 'before' | 'after' | 'error';
8687

87-
const wrappers: any = {
88+
type LegacyMap = { [ type in LegacyType ]: ReturnType< typeof convertHookData > };
89+
90+
type LegacyAdapter = HookFunction & { hooks: LegacyHookFunction[] };
91+
92+
type LegacyStore = {
93+
before: { [ method: string ]: LegacyAdapter },
94+
after: { [ method: string ]: LegacyAdapter },
95+
error: { [ method: string ]: LegacyAdapter },
96+
hooks: { [ method: string ]: HookFunction[] }
97+
};
98+
99+
const types: LegacyType[] = ['before', 'after', 'error'];
100+
101+
const isType = (value: any): value is LegacyType => types.includes(value);
102+
103+
const wrappers = {
88104
before: fromBeforeHooks,
89105
after: fromAfterHooks,
90106
error: fromErrorHooks,
91107
};
92108

93-
// Add `.hooks` functionality to an object
94-
export function enableLegacyHooks (
95-
obj: any,
96-
methods: string[] = defaultServiceMethods
97-
) {
98-
const hookData: any = {hooks: {}};
99-
100-
for (const type of types) {
101-
hookData[type] = {};
102-
}
109+
const createStore = (methods: string[]) => {
110+
const store: LegacyStore = {
111+
before: {},
112+
after: {},
113+
error: {},
114+
hooks: {}
115+
};
103116

104117
for (const method of methods) {
105-
hookData.hooks[method] = [];
118+
store.hooks[method] = [];
106119
}
107120

108-
// Add non-enumerable `__hooks` property to the object
109-
Object.defineProperty(obj, '__hooks', {
121+
return store;
122+
};
123+
124+
const setStore = (object: any, store: LegacyStore) => {
125+
Object.defineProperty(object, '__hooks', {
110126
configurable: true,
111-
value: hookData,
127+
value: store,
112128
writable: true
113129
});
130+
};
114131

115-
return function legacyHooks (this: any, allHooks: LegacyHookMap<any, any>) {
116-
const touched = new Set<string>();
132+
const getStore = (object: any): LegacyStore => object.__hooks;
117133

118-
for (const [type, current] of Object.entries(allHooks)) {
119-
if (!types.includes(type)) {
120-
throw new Error(`'${type}' is not a valid hook type`);
121-
}
134+
const createMap = (input: LegacyHookMap<any, any>, methods: string[]) => {
135+
const map = {} as LegacyMap;
122136

123-
const hooks = convertHookData(current);
137+
for (const type of Object.keys(input)) {
138+
if (!isType(type)) {
139+
throw new Error(`'${type}' is not a valid hook type`);
140+
}
124141

125-
for (const method of Object.keys(hooks)) {
126-
if (method !== 'all' && !methods.includes(method)) {
127-
throw new Error(`'${method}' is not a valid hook method`);
128-
}
142+
const data = convertHookData(input[type]);
143+
144+
for (const method of Object.keys(data)) {
145+
if (method !== 'all' && !methods.includes(method)) {
146+
throw new Error(`'${method}' is not a valid hook method`);
129147
}
148+
}
149+
150+
map[type] = data;
151+
}
152+
153+
return map;
154+
};
130155

131-
for (const method of methods) {
132-
if (!hooks.all?.length && !hooks[method]?.length) continue;
156+
const createAdapter = (type: LegacyType) => {
157+
const hooks: LegacyHookFunction[] = [];
158+
const hook = wrappers[type](hooks);
159+
const adapter = Object.assign(hook, { hooks });
133160

134-
const hook = this.__hooks[type][method] ||= (() => {
135-
const hooks: LegacyHookFunction[] = [];
136-
const hook = wrappers[type](hooks);
137-
hook.hooks = hooks;
138-
touched.add(method);
139-
return hook;
140-
})();
161+
return adapter;
162+
};
163+
164+
const updateStore = (store: LegacyStore, map: LegacyMap) => {
165+
for (const method of Object.keys(store.hooks)) {
166+
let adapted = false;
167+
168+
for (const key of Object.keys(map)) {
169+
const type = key as LegacyType;
170+
const allHooks = map[type].all || [];
171+
const methodHooks = map[type][method] || [];
141172

142-
hook.hooks.push(...(hooks.all || []), ...(hooks[method] || []));
173+
if (allHooks.length || methodHooks.length) {
174+
const adapter = store[type][method] ||= (adapted = true, createAdapter(type));
175+
176+
adapter.hooks.push(...allHooks, ...methodHooks);
143177
}
144178
}
145179

146-
for (const method of touched) {
147-
const before = this.__hooks.before[method];
148-
const after = this.__hooks.after[method];
149-
const error = this.__hooks.error[method];
180+
if (adapted) {
181+
store.hooks[method] = [
182+
store.error[method],
183+
store.before[method],
184+
store.after[method]
185+
].filter(hook => hook);
186+
}
187+
}
188+
};
150189

151-
const hooks: HookFunction[] = [];
152-
if (error) hooks.push(error);
153-
if (before) hooks.push(before);
154-
if (after) hooks.push(after);
190+
// Add `.hooks` functionality to an object
191+
export function enableLegacyHooks (
192+
object: any,
193+
methods: string[] = defaultServiceMethods
194+
) {
195+
const store = createStore(methods);
155196

156-
this.__hooks.hooks[method] = hooks;
157-
}
197+
setStore(object, store);
198+
199+
return function legacyHooks (this: any, input: LegacyHookMap<any, any>) {
200+
const store = getStore(this);
201+
const map = createMap(input, methods);
202+
203+
updateStore(store, map);
158204

159205
return this;
160206
}

0 commit comments

Comments
 (0)