Skip to content

Commit ceca65c

Browse files
ssutarpzuraq
authored andcommitted
Add support for runtime data (#32)
Add support for runtime data, more details at: #17
1 parent f0bda10 commit ceca65c

File tree

12 files changed

+368
-49
lines changed

12 files changed

+368
-49
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"keywords": [
1010
"codemod-cli"
1111
],
12+
"license": "MIT",
1213
"dependencies": {
1314
"codemod-cli": "ssutar/codemod-cli",
1415
"minimatch": "^3.0.4",

transforms/ember-object/__testfixtures__/decorators.output.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { layout, className, classNames, tagName, attribute } from "@ember-decorators/component";
1+
import { attribute, className, classNames, layout, tagName } from "@ember-decorators/component";
22
import { sum as add, overridableReads as enoWay, overridableReads, reads, alias } from "@ember-decorators/object/computed";
33
import { get, set } from "@ember/object";
44
import { action, readOnly, volatile, computed, observes as watcher } from "@ember-decorators/object";
@@ -36,7 +36,13 @@ class Foo extends EmberObject {
3636

3737
@action
3838
baz() {
39-
super.baz(...arguments);
39+
// TODO: This call to super is within an action, and has to refer to the parent
40+
// class's actions to be safe. This should be refactored to call a normal method
41+
// on the parent class. If the parent class has not been converted to native
42+
// classes, it may need to be refactored as well. See
43+
// https: //github.com/scalvert/ember-es6-class-codemod/blob/master/README.md
44+
// for more details.
45+
super.actions.baz.call(this, ...arguments);
4046
}
4147

4248
@action
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"data": [
3+
{
4+
"./transforms/ember-object/__testfixtures__/runtime.input.js": {
5+
"computedProperties": [],
6+
"observedProperties": [],
7+
"observerProperties": {},
8+
"offProperties": { "offProp": ["prop1", "prop2"] },
9+
"overriddenActions": ["overriddenActionMethod"],
10+
"overriddenProperties": ["overriddenMethod"],
11+
"ownProperties": [],
12+
"type": "EmberObject",
13+
"unobservedProperties": { "unobservedProp": ["prop3", "prop4"] }
14+
}
15+
}
16+
]
17+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Program comments
3+
*/
4+
const Foo = Test.extend(MyMixin, {
5+
/**
6+
* Property comments
7+
*/
8+
prop: "defaultValue",
9+
boolProp: true,
10+
numProp: 123,
11+
[MY_VAL]: "val",
12+
queryParams: {},
13+
14+
unobservedProp: null,
15+
offProp: null,
16+
17+
/**
18+
* Method comments
19+
*/
20+
method() {
21+
// do things
22+
},
23+
24+
otherMethod: function() {},
25+
26+
get accessor() {
27+
return this._value;
28+
},
29+
30+
set accessor(value) {
31+
this._value = value;
32+
},
33+
34+
anotherMethod() {
35+
this._super(...arguments);
36+
},
37+
38+
overriddenMethod() {
39+
this._super(...arguments);
40+
},
41+
42+
actions: {
43+
actionMethod() {
44+
this._super(...arguments) && this.boolProp;
45+
},
46+
47+
overriddenActionMethod() {
48+
this._super(...arguments) && this.boolProp;
49+
}
50+
}
51+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"decorators": true,
3+
"runtime-config-path": "./transforms/ember-object/__testfixtures__/runtime.data.json"
4+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { action, off, unobserves } from "@ember-decorators/object";
2+
3+
/**
4+
* Program comments
5+
*/
6+
class Foo extends Test.extend(MyMixin) {
7+
/**
8+
* Property comments
9+
*/
10+
prop = "defaultValue";
11+
12+
boolProp = true;
13+
numProp = 123;
14+
[MY_VAL] = "val";
15+
queryParams = {};
16+
17+
@unobserves("prop3", "prop4")
18+
unobservedProp;
19+
20+
@off("prop1", "prop2")
21+
offProp;
22+
23+
/**
24+
* Method comments
25+
*/
26+
method() {
27+
// do things
28+
}
29+
30+
otherMethod() {}
31+
32+
get accessor() {
33+
return this._value;
34+
}
35+
36+
set accessor(value) {
37+
this._value = value;
38+
}
39+
40+
anotherMethod() {
41+
undefined;
42+
}
43+
44+
overriddenMethod() {
45+
super.overriddenMethod(...arguments);
46+
}
47+
48+
@action
49+
actionMethod() {
50+
undefined && this.boolProp;
51+
}
52+
53+
@action
54+
overriddenActionMethod() {
55+
// TODO: This call to super is within an action, and has to refer to the parent
56+
// class's actions to be safe. This should be refactored to call a normal method
57+
// on the parent class. If the parent class has not been converted to native
58+
// classes, it may need to be refactored as well. See
59+
// https: //github.com/scalvert/ember-es6-class-codemod/blob/master/README.md
60+
// for more details.
61+
super.actions.overriddenActionMethod.call(this, ...arguments) && this.boolProp;
62+
}
63+
}

transforms/ember-object/index.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
const { getOptions } = require("codemod-cli");
22
const { replaceEmberObjectExpressions } = require("../helpers/parse-helper");
3+
const { getRuntimeData } = require("../helpers/util");
34

4-
module.exports = function transformer(file, api, options) {
5+
module.exports = function transformer(file, api, opts) {
56
const j = api.jscodeshift;
6-
const root = j(file.source);
7+
const options = Object.assign({}, opts, getOptions());
8+
let { source, path } = file;
9+
const runtimeConfigPath = options["runtime-config-path"];
710

8-
replaceEmberObjectExpressions(
9-
j,
10-
root,
11-
file.path,
12-
Object.assign({}, options, getOptions())
13-
);
11+
if (runtimeConfigPath) {
12+
options.runtimeData = getRuntimeData(runtimeConfigPath, path);
13+
if (!options.runtimeData) {
14+
return;
15+
}
16+
}
17+
18+
const root = j(source);
19+
20+
replaceEmberObjectExpressions(j, root, path, options);
1421

1522
return root.toSource();
1623
};

transforms/helpers/EOProp.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class EOProp {
1616
this._prop = eoProp;
1717
this.decoratorNames = [];
1818
this.modifiers = [];
19+
this.decoratorArgs = {};
1920
}
2021

2122
get value() {
@@ -118,6 +119,18 @@ class EOProp {
118119
return this.decoratorNames.includes("attribute");
119120
}
120121

122+
get hasUnobservesDecorator() {
123+
return this.decoratorNames.includes("unobserves");
124+
}
125+
126+
get hasOffDecorator() {
127+
return this.decoratorNames.includes("off");
128+
}
129+
130+
get hasRuntimeData() {
131+
return !!this.runtimeType;
132+
}
133+
121134
setCallExpressionProps() {
122135
let calleeObject = get(this._prop, "value");
123136
const modifiers = [getModifier(calleeObject)];
@@ -158,6 +171,36 @@ class EOProp {
158171
this.value.name = value;
159172
}
160173
}
174+
175+
setRuntimeData({
176+
// computedProperties = [],
177+
// observedProperties = [],
178+
// observerProperties = {},
179+
offProperties = {},
180+
overriddenActions = [],
181+
overriddenProperties = [],
182+
// ownProperties = [],
183+
type = "",
184+
unobservedProperties = {}
185+
}) {
186+
if (!type) {
187+
return;
188+
}
189+
const name = this.name;
190+
if (Object.keys(unobservedProperties).includes(name)) {
191+
this.decoratorNames.push("unobserves");
192+
this.decoratorArgs["unobserves"] = unobservedProperties[name];
193+
}
194+
if (Object.keys(offProperties).includes(name)) {
195+
this.decoratorNames.push("off");
196+
this.decoratorArgs["off"] = offProperties[name];
197+
}
198+
if (this.isAction) {
199+
this.overriddenActions = overriddenActions;
200+
}
201+
this.isOverridden = overriddenProperties.includes(name);
202+
this.runtimeType = type;
203+
}
161204
}
162205

163206
module.exports = EOProp;

transforms/helpers/decorator-helper.js

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,25 @@ function createCallExpressionDecorators(j, decoratorName, instanceProp) {
6565
);
6666
}
6767

68+
function createDecoratorsWithArgs(j, identifier, args) {
69+
return [
70+
j.decorator(
71+
j.callExpression(
72+
j.identifier(identifier),
73+
args.map(arg => j.literal(arg))
74+
)
75+
)
76+
];
77+
}
78+
6879
/**
6980
* Create `@action` decorator
7081
*
7182
* @param {Object} j - jscodeshift lib reference
7283
* @returns {Decorator[]}
7384
*/
74-
function createActionDecorators(j) {
75-
return [j.decorator(j.identifier("action"))];
85+
function createIdentifierDecorators(j, identifier = "action") {
86+
return [j.decorator(j.identifier(identifier))];
7687
}
7788

7889
/**
@@ -102,25 +113,39 @@ function createBindingDecorators(j, decoratorName, instanceProp) {
102113
* @returns {Decorator[]}
103114
*/
104115
function createInstancePropDecorators(j, instanceProp) {
105-
return instanceProp.decoratorNames.reduce((decorators, decoratorName) => {
106-
if (!decoratorName) {
116+
return instanceProp.decoratorNames.reduce((decorators, decorator) => {
117+
if (!decorator) {
107118
return decorators;
108119
}
109-
if (decoratorName === "className" || decoratorName === "attribute") {
120+
if (decorator === "className" || decorator === "attribute") {
121+
return decorators.concat(
122+
createBindingDecorators(j, decorator, instanceProp)
123+
);
124+
}
125+
if (decorator === "off" || decorator === "unobserves") {
110126
return decorators.concat(
111-
createBindingDecorators(j, decoratorName, instanceProp)
127+
createDecoratorsWithArgs(
128+
j,
129+
decorator,
130+
instanceProp.decoratorArgs[decorator]
131+
)
112132
);
113133
}
114134
return decorators.concat(
115-
createCallExpressionDecorators(j, decoratorName, instanceProp)
135+
createCallExpressionDecorators(
136+
j,
137+
decorator,
138+
instanceProp,
139+
instanceProp.decoratorArgs[decorator]
140+
)
116141
);
117142
}, []);
118143
}
119144

120145
module.exports = {
121146
withDecorators,
122147
createClassDecorator,
123-
createActionDecorators,
148+
createIdentifierDecorators,
124149
createCallExpressionDecorators,
125150
createInstancePropDecorators
126151
};

transforms/helpers/parse-helper.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ const logger = require("./log-helper");
3434
* @param {ObjectExpression} emberObjectExpression
3535
* @returns {Object} Object of instance and function properties
3636
*/
37-
function getEmberObjectProps(j, eoExpression, importedDecoratedProps = {}) {
37+
function getEmberObjectProps(
38+
j,
39+
eoExpression,
40+
importedDecoratedProps = {},
41+
runtimeData = {}
42+
) {
3843
const objProps = get(eoExpression, "properties") || [];
3944

4045
const instanceProps = [];
@@ -55,6 +60,7 @@ function getEmberObjectProps(j, eoExpression, importedDecoratedProps = {}) {
5560
);
5661
} else {
5762
prop.setDecorators(importedDecoratedProps);
63+
prop.setRuntimeData(runtimeData);
5864
instanceProps.push(prop);
5965
}
6066
if (prop.isLayout) {
@@ -224,6 +230,8 @@ function getDecoratorsToImport(instanceProps, decoratorsMap = {}) {
224230
tagName: specs.tagName || prop.isTagName,
225231
className: specs.className || prop.hasClassNameDecorator,
226232
classNames: specs.classNames || prop.isClassNames,
233+
unobserves: specs.unobserves || prop.hasUnobservesDecorator,
234+
off: specs.off || prop.hasOffDecorator,
227235
volatile: specs.volatile || prop.hasVolatile
228236
};
229237
}, decoratorsMap);
@@ -513,7 +521,8 @@ function replaceEmberObjectExpressions(j, root, filePath, options = {}) {
513521
const eoProps = getEmberObjectProps(
514522
j,
515523
eoExpression,
516-
importedDecoratedProps
524+
importedDecoratedProps,
525+
options.runtimeData
517526
);
518527

519528
const errors = hasValidProps(eoProps, getOptions(options));

0 commit comments

Comments
 (0)