ember-legacy-class-transform
Version:
The default blueprint for ember-cli addons.
153 lines (123 loc) • 4.16 kB
JavaScript
import Ember from 'ember';
import EmberObject, { computed, observer, get, setProperties } from '@ember/object';
import Component from '@ember/component';
import { on } from '@ember/object/evented';
// import { getOwner } from '@ember/application';
import WeakMap from 'ember-weakmap';
import getValue from './get-value';
import { collapseKeysWithMap } from './collapse-keys';
import flattenKeys from './flatten-keys';
import {
ARRAY_EACH,
ARRAY_LENGTH
} from './-constants';
const { defineProperty, meta } = Ember;
const PROPERTIES = new WeakMap();
function findOrCreatePropertyInstance(context, propertyClass, key, cp) {
let propertiesForContext = PROPERTIES.get(context);
if (!propertiesForContext) {
propertiesForContext = new WeakMap();
PROPERTIES.set(context, propertiesForContext);
}
let property = propertiesForContext.get(cp);
if (property) {
return property;
}
// let owner = getOwner(context);
property = propertyClass.create(/* owner.ownerInjection(), */{
key,
context,
nonStrings: EmberObject.create()
});
propertiesForContext.set(cp, property);
if (context instanceof Component) {
context.one('willDestroyElement', () => {
property.destroy();
});
}
return property;
}
const BaseClass = EmberObject.extend({
computedDidChange: observer('computed', function() {
let { context, key } = this;
if (context.isDestroying) {
// controllers can get into this state
this.destroy();
return;
}
// try our best to detect and prevent a double render
let _meta = meta(context);
// no longer needed in ember 2.12
if (_meta.readableLastRendered) {
let lastRendered = _meta.readableLastRendered();
if (lastRendered && Object.hasOwnProperty.call(lastRendered, key)) {
return;
}
}
context.notifyPropertyChange(key);
})
});
function resolveMappedLocation(key, i) {
if (typeof key === 'string') {
return `context.${key}`;
} else {
return `nonStrings.${i}`;
}
}
export default function(observerBools, macroGenerator) {
return function(...keys) {
let { collapsedKeys, keyMap } = collapseKeysWithMap(keys);
function getOriginalArrayDecorator(key, i) {
if (typeof key === 'string') {
let originalKey = keys[keyMap[i]];
if (originalKey.indexOf(ARRAY_EACH) !== -1 || originalKey.indexOf(ARRAY_LENGTH) !== -1) {
return originalKey;
}
}
return key;
}
let mappedKeys = [];
function rewriteComputed(obj, key) {
let mappedWithResolvedOberverKeys = mappedKeys.map((macro, i) => {
let shouldObserve = observerBools[i];
if (shouldObserve) {
macro = getValue({ context: this, macro, key });
}
return macro;
});
let cp = macroGenerator.apply(this, mappedWithResolvedOberverKeys);
defineProperty(this, 'computed', cp);
}
let classProperties = {};
collapsedKeys.forEach((key, i) => {
let shouldObserve = observerBools[i];
if (!shouldObserve) {
key = getOriginalArrayDecorator(key, i);
}
let mappedKey = resolveMappedLocation(key, i);
mappedKeys.push(mappedKey);
if (shouldObserve) {
classProperties[`key${i}DidChange`] = observer(mappedKey, rewriteComputed);
}
});
let ObserverClass = BaseClass.extend(classProperties, {
// can't use rewriteComputed directly, maybe a bug
// https://github.com/emberjs/ember.js/issues/15246
onInit: on('init', function() {
rewriteComputed.call(this);
})
});
let cp = computed(...flattenKeys(keys), function(key) {
let propertyInstance = findOrCreatePropertyInstance(this, ObserverClass, key, cp);
let properties = collapsedKeys.reduce((properties, macro, i) => {
if (typeof macro !== 'string') {
properties[i.toString()] = getValue({ context: this, macro, key });
}
return properties;
}, {});
setProperties(propertyInstance.nonStrings, properties);
return get(propertyInstance, 'computed');
}).readOnly();
return cp;
};
}