aurelia-metadata
Version: 
Utilities for reading and writing the metadata of JavaScript functions.
251 lines (205 loc) • 6.81 kB
JavaScript
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
import { PLATFORM } from 'aurelia-pal';
function isObject(val) {
  return val && (typeof val === 'function' || typeof val === 'object');
}
export const metadata = {
  resource: 'aurelia:resource',
  paramTypes: 'design:paramtypes',
  propertyType: 'design:type',
  properties: 'design:properties',
  get(metadataKey, target, targetKey) {
    if (!isObject(target)) {
      return undefined;
    }
    let result = metadata.getOwn(metadataKey, target, targetKey);
    return result === undefined ? metadata.get(metadataKey, Object.getPrototypeOf(target), targetKey) : result;
  },
  getOwn(metadataKey, target, targetKey) {
    if (!isObject(target)) {
      return undefined;
    }
    return Reflect.getOwnMetadata(metadataKey, target, targetKey);
  },
  define(metadataKey, metadataValue, target, targetKey) {
    Reflect.defineMetadata(metadataKey, metadataValue, target, targetKey);
  },
  getOrCreateOwn(metadataKey, Type, target, targetKey) {
    let result = metadata.getOwn(metadataKey, target, targetKey);
    if (result === undefined) {
      result = new Type();
      Reflect.defineMetadata(metadataKey, result, target, targetKey);
    }
    return result;
  }
};
const originStorage = new Map();
const unknownOrigin = Object.freeze({ moduleId: undefined, moduleMember: undefined });
export let Origin = class Origin {
  constructor(moduleId, moduleMember) {
    this.moduleId = moduleId;
    this.moduleMember = moduleMember;
  }
  static get(fn) {
    let origin = originStorage.get(fn);
    if (origin === undefined) {
      PLATFORM.eachModule((key, value) => {
        if (typeof value === 'object') {
          let isBrowserWindow = typeof window !== 'undefined' && value === window;
          for (let name in value) {
            if (isBrowserWindow && name === 'webkitStorageInfo') {
              continue;
            }
            try {
              let exp = value[name];
              if (exp === fn) {
                originStorage.set(fn, origin = new Origin(key, name));
                return true;
              }
            } catch (e) {}
          }
        }
        if (value === fn) {
          originStorage.set(fn, origin = new Origin(key, 'default'));
          return true;
        }
        return false;
      });
    }
    return origin || unknownOrigin;
  }
  static set(fn, origin) {
    originStorage.set(fn, origin);
  }
};
export function decorators(...rest) {
  let applicator = function (target, key, descriptor) {
    let i = rest.length;
    if (key) {
      descriptor = descriptor || {
        value: target[key],
        writable: true,
        configurable: true,
        enumerable: true
      };
      while (i--) {
        descriptor = rest[i](target, key, descriptor) || descriptor;
      }
      Object.defineProperty(target, key, descriptor);
    } else {
      while (i--) {
        target = rest[i](target) || target;
      }
    }
    return target;
  };
  applicator.on = applicator;
  return applicator;
}
export function deprecated(optionsOrTarget, maybeKey, maybeDescriptor) {
  function decorator(target, key, descriptor) {
    const methodSignature = `${target.constructor.name}#${key}`;
    let options = maybeKey ? {} : optionsOrTarget || {};
    let message = `DEPRECATION - ${methodSignature}`;
    if (typeof descriptor.value !== 'function') {
      throw new SyntaxError('Only methods can be marked as deprecated.');
    }
    if (options.message) {
      message += ` - ${options.message}`;
    }
    return _extends({}, descriptor, {
      value: function deprecationWrapper() {
        if (options.error) {
          throw new Error(message);
        } else {
          console.warn(message);
        }
        return descriptor.value.apply(this, arguments);
      }
    });
  }
  return maybeKey ? decorator(optionsOrTarget, maybeKey, maybeDescriptor) : decorator;
}
export function mixin(behavior) {
  const instanceKeys = Object.keys(behavior);
  function _mixin(possible) {
    let decorator = function (target) {
      let resolvedTarget = typeof target === 'function' ? target.prototype : target;
      let i = instanceKeys.length;
      while (i--) {
        let property = instanceKeys[i];
        Object.defineProperty(resolvedTarget, property, {
          value: behavior[property],
          writable: true
        });
      }
    };
    return possible ? decorator(possible) : decorator;
  }
  return _mixin;
}
function alwaysValid() {
  return true;
}
function noCompose() {}
function ensureProtocolOptions(options) {
  if (options === undefined) {
    options = {};
  } else if (typeof options === 'function') {
    options = {
      validate: options
    };
  }
  if (!options.validate) {
    options.validate = alwaysValid;
  }
  if (!options.compose) {
    options.compose = noCompose;
  }
  return options;
}
function createProtocolValidator(validate) {
  return function (target) {
    let result = validate(target);
    return result === true;
  };
}
function createProtocolAsserter(name, validate) {
  return function (target) {
    let result = validate(target);
    if (result !== true) {
      throw new Error(result || `${name} was not correctly implemented.`);
    }
  };
}
export function protocol(name, options) {
  options = ensureProtocolOptions(options);
  let result = function (target) {
    let resolvedTarget = typeof target === 'function' ? target.prototype : target;
    options.compose(resolvedTarget);
    result.assert(resolvedTarget);
    Object.defineProperty(resolvedTarget, 'protocol:' + name, {
      enumerable: false,
      configurable: false,
      writable: false,
      value: true
    });
  };
  result.validate = createProtocolValidator(options.validate);
  result.assert = createProtocolAsserter(name, options.validate);
  return result;
}
protocol.create = function (name, options) {
  options = ensureProtocolOptions(options);
  let hidden = 'protocol:' + name;
  let result = function (target) {
    let decorator = protocol(name, options);
    return target ? decorator(target) : decorator;
  };
  result.decorates = function (obj) {
    return obj[hidden] === true;
  };
  result.validate = createProtocolValidator(options.validate);
  result.assert = createProtocolAsserter(name, options.validate);
  return result;
};