Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removes @className and @attribute in favor of @classNameBindings and @attributeBindings #176

Merged
merged 1 commit into from
Aug 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions transforms/ember-object/__testfixtures__/decorators.output.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import classic from 'ember-classic-decorator';
import { attribute, className, classNames, tagName, layout as templateLayout } from '@ember-decorators/component';

import {
classNames,
attributeBindings,
classNameBindings,
tagName,
layout as templateLayout,
} from '@ember-decorators/component';

import { observes as watcher, on } from '@ember-decorators/object';
import { inject as controller } from '@ember/controller';
import { inject as service } from '@ember/service';
Expand Down Expand Up @@ -80,20 +88,16 @@ class Foo extends EmberObject {
}

@classic
@classNameBindings('isEnabled:enabled:disabled', 'a:b:c', 'c:d')
@attributeBindings('customHref:href')
class Comp extends EmberObject {
@computed('a', 'c')
@className('enabled', 'disabled')
get isEnabled() {
return false;
}

@className('b', 'c')
a = true;

@className('d')
c = '';

@attribute('href')
customHref = 'http://emberjs.com';
}

Expand Down
131 changes: 56 additions & 75 deletions transforms/helpers/EOProp.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,60 @@ const {
* A wrapper object for ember object properties
*/
class EOProp {
constructor(eoProp) {
constructor(eoProp, runtimeData, importedDecoratedProps) {
this._prop = eoProp;
this.decorators = [];
this.modifiers = [];
this.decoratorArgs = {};

if (runtimeData.type) {
const {
type,
computedProperties = [],
offProperties = {},
overriddenActions = [],
overriddenProperties = [],
unobservedProperties = {},
} = runtimeData;

this.emberType = type;

const name = this.name;
if (Object.keys(unobservedProperties).includes(name)) {
this.decorators.push({ name: 'unobserves' });
this.decoratorArgs['unobserves'] = unobservedProperties[name];
}
if (Object.keys(offProperties).includes(name)) {
this.decorators.push({ name: 'off' });
this.decoratorArgs['off'] = offProperties[name];
}
if (computedProperties.includes(name)) {
this.isComputed = true;
}
if (this.isActions) {
this.overriddenActions = overriddenActions;
}
this.isOverridden = overriddenProperties.includes(name);
this.runtimeType = type;
}

if (this.isCallExpression) {
let calleeObject = get(this._prop, 'value');
const modifiers = [getModifier(calleeObject)];
while (get(calleeObject, 'callee.type') === 'MemberExpression') {
calleeObject = get(calleeObject, 'callee.object');
modifiers.push(getModifier(calleeObject));
}
this.calleeObject = calleeObject;
this.modifiers = modifiers.reverse();
this.modifiers.shift();

if (importedDecoratedProps[this.calleeName]) {
this.decorators.push(importedDecoratedProps[this.calleeName]);
} else if (this.isComputed) {
this.decorators.push({ name: this.calleeName });
}
}
}

get value() {
Expand Down Expand Up @@ -131,16 +180,16 @@ class EOProp {
return this.name === 'classNames';
}

get isAction() {
return this.name === 'actions';
get isClassNameBindings() {
return this.name === 'classNameBindings';
}

get hasClassNameDecorator() {
return this.decoratorNames.includes('className');
get isAttributeBindings() {
return this.name === 'attributeBindings';
}

get hasAttributeDecorator() {
return this.decoratorNames.includes('attribute');
get isActions() {
return this.name === 'actions';
}

get hasUnobservesDecorator() {
Expand All @@ -162,74 +211,6 @@ class EOProp {
get hasMetaDecorator() {
return this.decorators.find(d => d.isMetaDecorator);
}

setCallExpressionProps() {
let calleeObject = get(this._prop, 'value');
const modifiers = [getModifier(calleeObject)];
while (get(calleeObject, 'callee.type') === 'MemberExpression') {
calleeObject = get(calleeObject, 'callee.object');
modifiers.push(getModifier(calleeObject));
}
this.calleeObject = calleeObject;
this.modifiers = modifiers.reverse();
this.modifiers.shift();
}

setDecorators(importedDecoratedProps) {
if (this.isCallExpression) {
this.setCallExpressionProps();

if (importedDecoratedProps[this.calleeName]) {
this.decorators.push(importedDecoratedProps[this.calleeName]);
} else if (this.isComputed) {
this.decorators.push({ name: this.calleeName });
}
}
}

addBindingProps(attributeBindingsProps, classNameBindingsProps) {
if (attributeBindingsProps[this.name]) {
this.decorators.push({ name: 'attribute' });
this.propList = attributeBindingsProps[this.name];
} else if (classNameBindingsProps[this.name]) {
this.decorators.push({ name: 'className' });
this.propList = classNameBindingsProps[this.name];
}
}

setRuntimeData({
computedProperties = [],
// observedProperties = [],
// observerProperties = {},
offProperties = {},
overriddenActions = [],
overriddenProperties = [],
// ownProperties = [],
type = '',
unobservedProperties = {},
}) {
if (!type) {
return;
}

const name = this.name;
if (Object.keys(unobservedProperties).includes(name)) {
this.decorators.push({ name: 'unobserves' });
this.decoratorArgs['unobserves'] = unobservedProperties[name];
}
if (Object.keys(offProperties).includes(name)) {
this.decorators.push({ name: 'off' });
this.decoratorArgs['off'] = offProperties[name];
}
if (computedProperties.includes(name)) {
this.isComputed = true;
}
if (this.isAction) {
this.overriddenActions = overriddenActions;
}
this.isOverridden = overriddenProperties.includes(name);
this.runtimeType = type;
}
}

module.exports = EOProp;
53 changes: 6 additions & 47 deletions transforms/helpers/parse-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,54 +24,13 @@ const logger = require('./log-helper');
function getEmberObjectProps(j, eoExpression, importedDecoratedProps = {}, runtimeData = {}) {
const objProps = get(eoExpression, 'properties') || [];

const instanceProps = [];
const attributeBindingsProps = {};
const classNameBindingsProps = {};

objProps.forEach(objProp => {
const prop = new EOProp(objProp);
if (prop.name === 'classNameBindings') {
Object.assign(classNameBindingsProps, parseBindingProps(prop.value.elements));
} else if (prop.name === 'attributeBindings') {
Object.assign(attributeBindingsProps, parseBindingProps(prop.value.elements));
} else {
prop.setRuntimeData(runtimeData);
prop.setDecorators(importedDecoratedProps);
instanceProps.push(prop);
}
});

// Assign decorator names to the binding props if any
instanceProps.forEach(instanceProp => {
instanceProp.addBindingProps(attributeBindingsProps, classNameBindingsProps);
});

return {
instanceProps,
instanceProps: objProps.map(
objProp => new EOProp(objProp, runtimeData, importedDecoratedProps)
),
};
}

/**
* Split the binding property values using `:` as separator
*
* For example ["isEnabled:enabled:disabled", "a:b:c", "c:d"] will be parsed as
* {
* isEnabled:["enabled", "disabled"],
* a: ["b", "c"],
* c: ["d"]
* }
*
* @param {Array} bindingPropElements
* @returns {Object}
*/
function parseBindingProps(bindingPropElements = []) {
return bindingPropElements.reduce((props, bindingElement) => {
const [boundPropName, ...bindingElementList] = bindingElement.value.split(':');
props[boundPropName.trim()] = bindingElementList;
return props;
}, {});
}

/**
* Get the map of decorators to import other than the computed props, services etc
* which already have imports in the code
Expand All @@ -82,10 +41,10 @@ function parseBindingProps(bindingPropElements = []) {
function getDecoratorsToImportMap(instanceProps, decoratorsMap = {}) {
return instanceProps.reduce((specs, prop) => {
return {
action: specs.action || prop.isAction,
attribute: specs.attribute || prop.hasAttributeDecorator,
className: specs.className || prop.hasClassNameDecorator,
action: specs.action || prop.isActions,
classNames: specs.classNames || prop.isClassNames,
classNameBindings: specs.classNameBindings || prop.isClassNameBindings,
attributeBindings: specs.attributeBindings || prop.isAttributeBindings,
layout: specs.layout || prop.isLayoutDecorator,
templateLayout: specs.templateLayout || prop.isTemplateLayoutDecorator,
off: specs.off || prop.hasOffDecorator,
Expand Down
8 changes: 5 additions & 3 deletions transforms/helpers/util/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ const EMBER_DECORATOR_SPECIFIERS = {
'@ember/object': ['action'],
'@ember-decorators/object': ['off', 'on', 'unobserves'],
'@ember-decorators/component': [
'attribute',
'className',
'classNames',
'attributeBindings',
'classNameBindings',
LAYOUT_DECORATOR_NAME,
'tagName',
LAYOUT_DECORATOR_LOCAL_NAME,
Expand Down Expand Up @@ -247,7 +247,9 @@ function startsWithUpperCaseLetter(word = '') {
* @returns boolean
*/
function isClassDecoratorProp(propName) {
return propName === 'tagName' || propName === 'classNames' || propName === 'layout';
return ['layout', 'tagName', 'classNames', 'classNameBindings', 'attributeBindings'].includes(
propName
);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion transforms/helpers/validation-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function hasValidProps(
);
}

if (instanceProp.isAction) {
if (instanceProp.isActions) {
errors = errors.concat(getLifecycleHookErrors(instanceProp));
errors = errors.concat(getInfiniteLoopErrors(j, instanceProp));
}
Expand Down