Sorry for the verbose title. I have a class MutateMe
passed into a factory called FilterFactory
by a decorator Decorator
.
export const Decorator = (options?: DecoratorOptions) => <T extends Constructor>(target: T) => {
new FilterFactory(target, options);
}
Within this factory, I'm copying over the methods onto the target
class and setting its metadata.
export class FilterFactory {
constructor(protected target: any, options: DecoratorOptions) {
// Getting the reference to the class from where I want to copy over methods with their own metadata
const routesController = FilterController;
// The class itself consists of a prefix that must be prepended to all its member methods' metadata.
const prefixRoute = getControllerPrefix(routesController);
console.log("For each key (member name)")
Reflect.ownKeys(routesController.prototype).forEach(
(property) => {
// Ignore the primitive class methods
if (!['constructor', 'toString', 'length'].includes(property.toString())) {
// Copy the methods over to the `target`
Object.defineProperty(
target.prototype,
property,
Object.getOwnPropertyDescriptor(
routesController.prototype,
property
)
)
// Prepends class metadata `filter` to each route method's metadata
patchRoutes(target.prototype[property], prefixRoute)
// NOTE: An alternative to prototype property assignment (Doesn't work either)
// target.prototype[property] = routesController.prototype[property]
console.log(Reflect.getOwnMetadata(PATH_METADATA, target.prototype[property]))
}
})
}
}
The patchRoutes
function is like so:
const patchRoutes = <K, T extends string, P>(patchee: any, patches: (T | T[] | ((...args: P[]) => (T | T[]))), ...args: P[]) => {
const existingPath = Reflect.getOwnMetadata(PATH_METADATA, patchee)
if (patches instanceof Function) { patches = patches(...args) }
if (!Array.isArray(patches)) patches = [patches]
Reflect.defineMetadata(PATH_METADATA, (existingPath === "/" ? [...patches] : [...patches, existingPath]).join("/"), patchee)
const createResetCallback = (resetValue, resetTarget) => () =>
Reflect.defineMetadata(PATH_METADATA, resetValue, resetTarget)
return createResetCallback(existingPath, patchee)
}
It returns a reset
callback to reset the patched metadata.
Now, when I decorate more than one classes with this decorator, I can see a duplication of patching.
For example, patching once would give me foo/filter/...
and for the second call, it'd give me bar/filter/filter/...
.
I wanted to see if it was the problem of copying methods over improperly, so, I tried patching the base class, copy over the patched methods and reset the metadata of the base class:
const propertyResetCb = patchRoutes(routesController.prototype[property], prefixRoute)
...
// Assigning the property now to the target
...
// Calling the reset callback
propertyResetCb()
However, this seems to reset the property of all the decorators that I've made.
This leads me to believe that it's using a singular prototype reference for the copied over methods. I'd wish to copy them free of reference (clone if you will) so that I can independently set their metadata.
Also, I'd prefer if I didn't have to modify the patchRoutes
to factor in the duplication because, in the end, I'd like to do more modifications to their individual metadata separately.
Thanks :)
Aucun commentaire:
Enregistrer un commentaire