aurelia-router-metadata
Version:
Adds decorator configuration with conventions and eager loading capabilities to aurelia-router
132 lines (112 loc) • 4.35 kB
text/typescript
import { PLATFORM } from "aurelia-pal";
import { $Application, $Constructor, $Export, $Module, $Property, $Prototype } from "./model";
import { allObjectKeys } from "./util";
export class Registry {
private cache: { [key: string]: $Module | undefined };
private moduleIds: Set<string>;
private $application: $Application;
constructor() {
this.cache = Object.create(null);
this.moduleIds = new Set();
this.$application = new $Application();
}
public getModule(normalizedId: string): $Module | undefined {
let $module = this.cache[normalizedId];
if ($module === undefined) {
let moduleExport: any;
PLATFORM.eachModule((moduleId: string, value: any) => {
if (moduleId === normalizedId) {
moduleExport = value;
return true;
} else {
return false;
}
});
if (moduleExport !== undefined) {
$module = this.registerModule(moduleExport, normalizedId);
}
}
return $module;
}
public registerModuleViaConstructor($constructor: Function): $Module {
let moduleInstance: Function | { [key: string]: Function } | undefined;
let moduleId: string | undefined;
PLATFORM.eachModule((key: string, value: any) => {
if (typeof value === "object") {
for (const name of Object.keys(value)) {
if (value[name] === $constructor) {
moduleInstance = value;
moduleId = key;
return true;
}
}
}
if (value === $constructor) {
moduleInstance = value;
moduleId = key;
return true;
} else {
return false;
}
});
if (!moduleInstance || !moduleId) {
throw new Error(`No module could be found for constructor ${$constructor}`);
}
return this.registerModule(moduleInstance, moduleId);
}
public registerModule(moduleInstance: Function | { [key: string]: Function }, moduleId: string): $Module {
this.moduleIds.add(moduleId);
const $module = (this.cache[moduleId] = new $Module(this.$application, moduleId, moduleInstance));
this.$application.addModule($module);
if (moduleInstance instanceof Function) {
this.registerModuleExport($module, "default", moduleInstance as Function);
} else {
for (const exportName of Object.keys(moduleInstance)) {
const exportValue = moduleInstance[exportName];
if (exportValue instanceof Function) {
this.registerModuleExport($module, exportName, exportValue);
}
}
}
return $module;
}
private registerModuleExport($module: $Module, exportName: string, exportValue: Function): void {
if (!Object.prototype.hasOwnProperty.call(exportValue, "prototype")) {
return;
}
const $export = new $Export($module, exportName);
$export.$constructor = new $Constructor($export, exportName, exportValue);
$export.$prototype = new $Prototype($export, exportName, exportValue.prototype);
$module.addExport($export);
if ($module.$defaultExport === null) {
$module.$defaultExport = $export;
}
this.registerProperties($export.$constructor);
this.registerProperties($export.$prototype);
const ownNeedsBase = $export.hasBase && $export.$base === null;
const ownRaw = $export.$constructor.raw;
const ownBase = Object.getPrototypeOf(ownRaw);
for (const $other of this.$application.$exports) {
const otherNeedsBase = $other.hasBase && $other.$base === null;
const otherRaw = $other.$constructor.raw;
if (ownNeedsBase && ownBase === otherRaw) {
$export.$constructor.$base = $other.$constructor;
$export.$prototype.$base = $other.$prototype;
} else if (otherNeedsBase) {
const otherBase = Object.getPrototypeOf(otherRaw);
if (otherBase === ownRaw) {
$other.$constructor.$base = $export.$constructor;
$other.$prototype.$base = $export.$prototype;
}
}
}
}
private registerProperties($object: $Constructor | $Prototype): void {
const obj = $object.raw;
for (const key of allObjectKeys(obj)) {
const descriptor = Object.getOwnPropertyDescriptor(obj, key) as PropertyDescriptor;
const propertySymbol = new $Property($object, key, descriptor);
$object.addProperty(propertySymbol);
}
}
}