Skip to content

Commit 4de99d2

Browse files
author
Robert Jackson
authored
[FEATURE] Replace dyfactor with puppeteer (#118)
[FEATURE] Replace dyfactor with puppeteer Co-authored-by: Robert Jackson <rjackson@linkedin.com>
2 parents de9418b + 8a1e70e commit 4de99d2

File tree

103 files changed

+17533
-89
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+17533
-89
lines changed

.eslintignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
**/__testfixtures__/**/*.js
1+
**/__testfixtures__/**/*.js
2+
**/fixtures/**

.eslintrc.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
module.exports = {
22
root: true,
3+
parserOptions: {
4+
ecmaVersion: 2017
5+
},
36
env: {
47
node: true,
58
es6: true

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
language: node_js
33
node_js:
4-
- "6"
4+
- "8"
55

66
sudo: false
77
dist: trusty
@@ -17,4 +17,4 @@ install:
1717
- yarn install
1818

1919
script:
20-
- yarn test
20+
- yarn test

bin/cli.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
#!/usr/bin/env node
22
"use strict";
33

4+
const gatherTelemetry = require("../lib/gather-telemetry");
5+
6+
gatherTelemetry(process.argv[2]);
7+
48
require("codemod-cli").runTransform(
59
__dirname,
6-
process.argv[2] /* transform name */,
10+
"ember-object",
711
process.argv.slice(2) /* paths or globs */
812
);

bin/temeletry.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env node
2+
"use strict";
3+
4+
const gatherTelemetry = require("../lib/gather-telemetry");
5+
6+
gatherTelemetry(process.argv[2]);

lib/cache.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const Cache = require("sync-disk-cache");
2+
const getRepoInfo = require("git-repo-info");
3+
4+
const gitInfo = getRepoInfo();
5+
const cache = new Cache(`native-class-codemod-${gitInfo.sha}-${process.cwd()}`);
6+
7+
module.exports = cache;

lib/gather-telemetry.js

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
const puppeteer = require("puppeteer");
2+
const cache = require("./cache");
3+
4+
module.exports = async function gatherTelemetry(url) {
5+
const browser = await puppeteer.launch();
6+
const page = await browser.newPage();
7+
8+
await page.goto(url);
9+
10+
// Get the "viewport" of the page, as reported by the page.
11+
const telemetry = await page.evaluate(() => {
12+
/* globals window, Ember */
13+
let telemetry = {};
14+
15+
const modules = Object.keys(window.require.entries);
16+
17+
for (let modulePath of modules) {
18+
let module = require(modulePath);
19+
20+
if (module && module.default && module.default.proto) {
21+
let defaultProto = module.default.proto();
22+
23+
telemetry[modulePath] = parseMeta(Ember.meta(defaultProto));
24+
}
25+
}
26+
27+
/**
28+
* Compares the object with types of Ember objects
29+
*
30+
* @param {Object} object
31+
* @returns {String} type
32+
*/
33+
function getType(object) {
34+
const types = [
35+
"Application",
36+
"Controller",
37+
"Route",
38+
"Component",
39+
"Service",
40+
"Router",
41+
"Engine"
42+
];
43+
return (
44+
types.find(type => Ember[type] && object instanceof Ember[type]) ||
45+
"EmberObject"
46+
);
47+
}
48+
49+
/**
50+
* Parses ember meta data object and collects the runtime information
51+
*
52+
* @param {Object} meta
53+
* @returns {Object} data - Parsed metadata for the ember object
54+
* @returns {String[]} data.computedProperties - list of computed properties
55+
* @returns {String[]} data.observedProperties - list of observed properties
56+
* @returns {Object} data.observerProperties - list of observer properties
57+
* @returns {Object} data.offProperties - list of observer properties
58+
* @returns {String[]} data.overriddenActions - list of overridden actions
59+
* @returns {String[]} data.overriddenProperties - list of overridden properties
60+
* @returns {String[]} data.ownProperties - list of object's own properties
61+
* @returns {String} data.type - type of ember object
62+
* @returns {Object} data.unobservedProperties - list of unobserved properties
63+
*/
64+
function parseMeta(meta = {}) {
65+
if (!meta || !meta.source) {
66+
return {};
67+
}
68+
const { source } = meta;
69+
const type = getType(source);
70+
71+
const ownProperties = Object.keys(source).filter(
72+
key => !["_super", "actions"].includes(key)
73+
);
74+
75+
const ownActions = source.actions ? Object.keys(source.actions) : [];
76+
77+
const observedProperties = Object.keys(meta._watching || {});
78+
79+
const overriddenProperties = ownProperties.filter(key =>
80+
isOverridden(meta.parent, key)
81+
);
82+
83+
const overriddenActions = ownActions.filter(key =>
84+
isActionOverridden(meta.parent, key)
85+
);
86+
87+
const computedProperties = [];
88+
meta.forEachDescriptors((name, desc) => {
89+
const descProto = Object.getPrototypeOf(desc) || {};
90+
const constructorName = descProto.constructor
91+
? descProto.constructor.name
92+
: "";
93+
if (
94+
desc.enumerable &&
95+
ownProperties.includes(name) &&
96+
constructorName === "ComputedProperty"
97+
) {
98+
computedProperties.push(name);
99+
}
100+
});
101+
102+
const { offProperties, unobservedProperties } = ownProperties.reduce(
103+
({ offProperties, unobservedProperties }, key) => {
104+
const { type, events } = getListenerData(meta.parent, key);
105+
if (type === "event") {
106+
offProperties[key] = events;
107+
} else if (type === "observer") {
108+
unobservedProperties[key] = events;
109+
}
110+
return { offProperties, unobservedProperties };
111+
},
112+
{
113+
offProperties: {},
114+
unobservedProperties: {}
115+
}
116+
);
117+
118+
const observerProperties = observedProperties.reduce((acc, oProp) => {
119+
const listener = meta.matchingListeners(`${oProp}:change`)[1];
120+
acc[listener] = [].concat(acc[listener] || [], [oProp]);
121+
return acc;
122+
}, {});
123+
124+
return {
125+
computedProperties,
126+
observedProperties,
127+
observerProperties,
128+
offProperties,
129+
overriddenActions,
130+
overriddenProperties,
131+
ownProperties,
132+
type,
133+
unobservedProperties
134+
};
135+
}
136+
137+
/**
138+
* Parses the ember meta with passed key
139+
*
140+
* @param {Ember.meta} map
141+
* @param {String} key
142+
* @returns {Object} meta - The listener meta data
143+
* @returns {String} meta.type - Type of listener can be observer|event
144+
* @returns {String[]} meta.events - name of events/properties the listener is registered on
145+
*/
146+
function getListenerData(map, key) {
147+
while (map) {
148+
let type = "event";
149+
const events = parseListeners(map._listeners).reduce(
150+
(acc, [event, , method]) => {
151+
if (method === key) {
152+
const [observedProp, observerEvent] = event.split(":");
153+
if (observerEvent) {
154+
type = "observer";
155+
}
156+
acc.push(observedProp);
157+
}
158+
return acc;
159+
},
160+
[]
161+
);
162+
if (events.length) {
163+
return {
164+
type,
165+
events
166+
};
167+
}
168+
map = map.parent;
169+
}
170+
return {};
171+
}
172+
173+
/**
174+
* Parse the listeners to a group of array of 4 elements
175+
*
176+
* @param {Array} listeners
177+
* @param {int} size
178+
* @returns Array
179+
*/
180+
function parseListeners(listeners = [], size = 4) {
181+
var result = [];
182+
if (listeners.length) {
183+
if (typeof listeners[0] === "object") {
184+
result = listeners.map(({ event, target, method, kind }) => [
185+
event,
186+
target,
187+
method,
188+
kind
189+
]);
190+
} else {
191+
const input = listeners.slice(0);
192+
while (input.length) {
193+
result.push(input.splice(0, size));
194+
}
195+
}
196+
}
197+
return result;
198+
}
199+
200+
/**
201+
* Checks if passed key is overriding any value from the parent objects
202+
*
203+
* @param {Object} map
204+
* @param {String} key
205+
* @returns boolean
206+
*/
207+
function isOverridden(map, key) {
208+
while (map) {
209+
const value = map.peekValues ? map.peekValues(key) : undefined;
210+
if (value !== undefined || (map.source && key in map.source)) {
211+
return true;
212+
}
213+
map = map.parent;
214+
}
215+
return false;
216+
}
217+
218+
/**
219+
* Checks if passed key is overriding any value from the parent objects' actions
220+
*
221+
* @param {Object} map
222+
* @param {String} key
223+
* @returns boolean
224+
*/
225+
function isActionOverridden(map, key) {
226+
while (map) {
227+
const { source } = map;
228+
if (source) {
229+
const { actions } = source;
230+
const value = actions ? actions[key] : undefined;
231+
if (value !== undefined) {
232+
return true;
233+
}
234+
}
235+
map = map.parent;
236+
}
237+
return false;
238+
}
239+
240+
return telemetry;
241+
});
242+
243+
cache.set("telemetry", JSON.stringify(telemetry));
244+
245+
await browser.close();
246+
};

package.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,39 @@
22
"name": "ember-es6-class-codemod",
33
"version": "0.1.3",
44
"scripts": {
5-
"test": "eslint . && codemod-cli test",
5+
"test": "eslint . && codemod-cli test && node ./test/run-test.js",
66
"update-docs": "codemod-cli update-docs"
77
},
88
"bin": "./bin/cli.js",
99
"keywords": [
1010
"codemod-cli"
1111
],
12+
"files": [
13+
"/bin",
14+
"/lib",
15+
"/transforms/helpers",
16+
"/transforms/ember-object/index.js"
17+
],
1218
"license": "MIT",
1319
"dependencies": {
1420
"codemod-cli": "^0.2.11",
21+
"fs-extra": "^8.0.1",
22+
"git-repo-info": "^2.1.0",
1523
"minimatch": "^3.0.4",
24+
"puppeteer": "^1.17.0",
25+
"sync-disk-cache": "^1.3.3",
26+
"walk-sync": "^1.1.3",
1627
"winston": "^3.2.1"
1728
},
1829
"devDependencies": {
1930
"eslint": "^5.16.0",
2031
"eslint-config-prettier": "^5.0.0",
2132
"eslint-plugin-prettier": "^3.1.0",
33+
"execa": "^1.0.0",
2234
"jest": "^24.8.0",
2335
"prettier": "^1.17.1"
36+
},
37+
"engines": {
38+
"node": "8.* || 10.* || >= 12.*"
2439
}
2540
}

test/fixtures/input/.editorconfig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# EditorConfig helps developers define and maintain consistent
2+
# coding styles between different editors and IDEs
3+
# editorconfig.org
4+
5+
root = true
6+
7+
8+
[*]
9+
end_of_line = lf
10+
charset = utf-8
11+
trim_trailing_whitespace = true
12+
insert_final_newline = true
13+
indent_style = space
14+
indent_size = 2
15+
16+
[*.hbs]
17+
insert_final_newline = false
18+
19+
[*.{diff,md}]
20+
trim_trailing_whitespace = false

test/fixtures/input/.ember-cli

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
/**
3+
Ember CLI sends analytics information by default. The data is completely
4+
anonymous, but there are times when you might want to disable this behavior.
5+
6+
Setting `disableAnalytics` to true will prevent any data from being sent.
7+
*/
8+
"disableAnalytics": false
9+
}

0 commit comments

Comments
 (0)