diff --git a/README.md b/README.md index 73af91f..04fda89 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ After `0.5.0`, a few breaking changes occured. ```javascript gatherTelemetryForUrl(url, gatherFunction, puppeteerArgs); ``` - + ## Caveats If the gather function references functions defined outside of the the gather function body, all of those functions must be exported as well. It is strongly suggested that the gather function be self contained, and if functions must be used (code maintainability/readability), that they be defined within the function. If this is not possible, the `gatherTelemetryForUrl` has been enhanced to accept all functions that must go along with the gather function: @@ -90,6 +90,18 @@ gatherTelemetryForUrl(appLocation, gatherFunction, suppportFn1, suppportFn2, ... * `yarn test` +### Debugging + +Debugging telemetry collection will require an app to collect telemetry from. + +For example, in the no-implicit-this codemod, one may add a `debugger;` in `analyzeEmberObject`, which runs inside of puppeteer, and have the `debugger;` / breakpoint triggered after booting an app and running a script like the following defined in the codemod: + +``` +"debug:telemetry": "DEBUG=true node --inspect-brk ./bin/telemetry.js", +``` + +`DEBUG=true` will pass the puppeteer options: `{ headless; false, devtools: true }`, allowing you to inspect telemetry gathering. Without `DEBUG=true`, it is only possible to debug the script that wraps the launching of puppeteer. + ## More info See "Gathering runtime data" section of diff --git a/lib/gather/analyze-ember-object.js b/lib/gather/analyze-ember-object.js index 7d76270..d9ed93f 100644 --- a/lib/gather/analyze-ember-object.js +++ b/lib/gather/analyze-ember-object.js @@ -1,24 +1,38 @@ -module.exports = function analyzeEmberObject(possibleEmberObject) { +module.exports = function analyzeEmberObject(possibleEmberObject, modulePath) { + const MODIFIERS_TOKEN = '/modifiers/'; + let isModifier = modulePath.includes(MODIFIERS_TOKEN); + if (typeof possibleEmberObject !== 'object' || possibleEmberObject === null) { return undefined; } let eObjDefault = possibleEmberObject.default; + let meta; if (eObjDefault) { if (eObjDefault.isHelperFactory) { return { type: 'Helper', }; - } else if (typeof eObjDefault.proto !== 'function') { + } + + // Potential for eObjDefault to be a native class + // with no superclass, such as a modifier + if (isModifier) { + meta = Ember.meta(eObjDefault); + } + + if (!meta && typeof eObjDefault.proto !== 'function') { return undefined; } } - let proto = possibleEmberObject.default.proto(); - // Ember here is assumed to be global when ran within the context of the browser /* globals Ember */ - let meta = Ember.meta(proto); + if (!meta) { + let proto = possibleEmberObject.default.proto(); + + meta = Ember.meta(proto); + } /** * Parses the ember meta with passed key @@ -120,7 +134,7 @@ module.exports = function analyzeEmberObject(possibleEmberObject) { return {}; } const { source } = meta; - const type = getType(source); + const type = getType(source, modulePath); const ownProperties = Object.keys(source).filter(key => !['_super', 'actions'].includes(key)); @@ -141,7 +155,11 @@ module.exports = function analyzeEmberObject(possibleEmberObject) { } }); - function getType(object) { + function getType(object /* , modulePath */) { + if (isModifier) { + return 'Modifier'; + } + const types = [ 'Application', 'Controller', @@ -153,7 +171,13 @@ module.exports = function analyzeEmberObject(possibleEmberObject) { 'Engine', ]; // eslint-disable-next-line no-undef - return types.find(type => Ember[type] && object instanceof Ember[type]) || 'EmberObject'; + let finiteType = types.find(type => Ember[type] && object instanceof Ember[type]); + + if (finiteType) { + return finiteType; + } + + return 'EmberObject'; } /** diff --git a/lib/gather/gather-telemetry.js b/lib/gather/gather-telemetry.js index 9c65c43..7552580 100644 --- a/lib/gather/gather-telemetry.js +++ b/lib/gather/gather-telemetry.js @@ -1,7 +1,15 @@ const puppeteer = require('puppeteer'); const { setTelemetry } = require('../utils/telemetry'); -const DEFAULT_PUPPETEER_ARGS = { ignoreHTTPSErrors: true }; +const DEFAULT_PUPPETEER_ARGS = Object.assign( + { ignoreHTTPSErrors: true }, + process.env.DEBUG + ? { + headless: false, + devtools: true, + } + : {} +); module.exports = async function gatherTelemetry(url, gatherFn, ...args) { let puppeteerArgs = [...args].pop(); @@ -12,6 +20,7 @@ module.exports = async function gatherTelemetry(url, gatherFn, ...args) { } else { puppeteerArgs = {}; } + Object.assign(puppeteerArgs, DEFAULT_PUPPETEER_ARGS); const browser = await puppeteer.launch(puppeteerArgs); @@ -42,7 +51,7 @@ module.exports = async function gatherTelemetry(url, gatherFn, ...args) { try { let module = require(modulePath); - telemetry[modulePath] = await gFn(module); + telemetry[modulePath] = await gFn(module, modulePath); } catch (error) { // log the error, but continue window.logErrorInNodeProcess(`error evaluating \`${modulePath}\`: ${error.message}`); diff --git a/lib/gather/gather-telemetry.test.js b/lib/gather/gather-telemetry.test.js index d46820f..a9ddbfe 100644 --- a/lib/gather/gather-telemetry.test.js +++ b/lib/gather/gather-telemetry.test.js @@ -49,7 +49,54 @@ describe('Provide a personalized `Gathering Function`', () => { test('can determine most Ember types with a robust function', async () => { await gatherTelemetry('http://localhost:4200', analyzeEmberObject); let telemetry = getTelemetry(); - expect(Object.values(telemetry).filter(Boolean).length).toEqual(34); + + let expected = [ + '@ember/render-modifiers/modifiers/did-insert', + '@ember/render-modifiers/modifiers/did-update', + '@ember/render-modifiers/modifiers/will-destroy', + 'ember-ajax/ajax-request', + 'ember-ajax/services/ajax', + 'ember-class-based-modifier/-private/modifier-classic', + 'ember-class-based-modifier/classic', + 'ember-data/adapter', + 'ember-data/adapters/json-api', + 'ember-data/adapters/rest', + 'ember-data/model', + 'ember-data/serializer', + 'ember-data/serializers/json-api', + 'ember-data/serializers/json', + 'ember-data/serializers/rest', + 'ember-data/store', + 'ember-data/transform', + 'ember-data/transforms/boolean', + 'ember-data/transforms/date', + 'ember-data/transforms/number', + 'ember-data/transforms/string', + 'ember-data/transforms/transform', + 'ember-inflector/lib/helpers/pluralize', + 'ember-inflector/lib/helpers/singularize', + 'ember-resolver/index', + 'ember-resolver/resolver', + 'ember-resolver/resolvers/classic/container-debug-adapter', + 'ember-resolver/resolvers/classic/index', + 'ember-welcome-page/components/welcome-page', + 'special-sauce/components/fire-sauce', + 'input/app', + 'input/components/test-component', + 'input/components/welcome-page', + 'input/helpers/app-version', + 'input/helpers/pluralize', + 'input/helpers/singularize', + 'input/modifiers/did-insert', + 'input/modifiers/did-update', + `input/modifiers/logger`, + 'input/modifiers/will-destroy', + 'input/resolver', + 'input/router', + 'input/services/ajax', + ]; + + expect(Object.keys(telemetry)).toEqual(expected); }); test('can handle external functions', async () => { diff --git a/package.json b/package.json index d232d43..35ed5c4 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ ], "main": "lib/index.js", "scripts": { + "debug": "DEBUG=true node scripts/debug.js", "lint:js": "eslint .", "test": "jest . --verbose --no-watchman --runInBand --forceExit" }, diff --git a/scripts/debug.js b/scripts/debug.js new file mode 100755 index 0000000..37f7950 --- /dev/null +++ b/scripts/debug.js @@ -0,0 +1,5 @@ +'use strict'; + +const { gatherTelemetryForUrl, analyzeEmberObject } = require('../lib'); + +gatherTelemetryForUrl(process.argv[2], analyzeEmberObject); diff --git a/test/fixtures/classic-app/app/modifiers/logger.js b/test/fixtures/classic-app/app/modifiers/logger.js new file mode 100644 index 0000000..89ec0b2 --- /dev/null +++ b/test/fixtures/classic-app/app/modifiers/logger.js @@ -0,0 +1,7 @@ +import Modifier from 'ember-class-based-modifier'; + +export default class LoggerModifier extends Modifier { + didInstall() { + console.log(this.element) + } +} diff --git a/test/fixtures/classic-app/package.json b/test/fixtures/classic-app/package.json index c44c0a1..0f7d480 100644 --- a/test/fixtures/classic-app/package.json +++ b/test/fixtures/classic-app/package.json @@ -20,8 +20,10 @@ "devDependencies": { "@ember/jquery": "^0.6.0", "@ember/optional-features": "^0.7.0", + "@ember/render-modifiers": "^1.0.2", "broccoli-asset-rev": "^3.0.0", "ember-ajax": "^5.0.0", + "ember-class-based-modifier": "^0.10.0", "ember-cli": "~3.10.1", "ember-cli-app-version": "^3.2.0", "ember-cli-babel": "^7.7.3", diff --git a/test/fixtures/classic-app/yarn.lock b/test/fixtures/classic-app/yarn.lock index d3e6f4b..b12360e 100644 --- a/test/fixtures/classic-app/yarn.lock +++ b/test/fixtures/classic-app/yarn.lock @@ -736,6 +736,14 @@ ember-cli-babel "^6.16.0" ember-compatibility-helpers "^1.1.1" +"@ember/render-modifiers@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@ember/render-modifiers/-/render-modifiers-1.0.2.tgz#2e87c48db49d922ce4850d707215caaac60d8444" + integrity sha512-6tEnHl5+62NTSAG2mwhGMFPhUrJQjoVqV+slsn+rlTknm2Zik+iwxBQEbwaiQOU1FUYxkS8RWcieovRNMR8inQ== + dependencies: + ember-cli-babel "^7.10.0" + ember-modifier-manager-polyfill "^1.1.0" + "@ember/test-helpers@^1.5.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@ember/test-helpers/-/test-helpers-1.6.0.tgz#e928c0190a19c312bf40dc8a6f4159735af48a7f" @@ -1341,6 +1349,13 @@ babel-plugin-debug-macros@^0.3.0: dependencies: semver "^5.3.0" +babel-plugin-ember-modules-api-polyfill@^2.12.0: + version "2.12.0" + resolved "https://registry.npmjs.org/babel-plugin-ember-modules-api-polyfill/-/babel-plugin-ember-modules-api-polyfill-2.12.0.tgz#a5e703205ba4e625a7fab9bb1aea64ef3222cf75" + integrity sha512-ZQU4quX0TJ1yYyosPy5PFigKdCFEVHJ6H0b3hwjxekIP9CDwzk0OhQuKhCOPti+d52VWjjCjxu2BrXEih29mFw== + dependencies: + ember-rfc176-data "^0.3.12" + babel-plugin-ember-modules-api-polyfill@^2.6.0, babel-plugin-ember-modules-api-polyfill@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/babel-plugin-ember-modules-api-polyfill/-/babel-plugin-ember-modules-api-polyfill-2.9.0.tgz#8503e7b4192aeb336b00265e6235258ff6b754aa" @@ -1932,6 +1947,23 @@ broccoli-babel-transpiler@^7.1.2, broccoli-babel-transpiler@^7.2.0: rsvp "^4.8.4" workerpool "^3.1.1" +broccoli-babel-transpiler@^7.3.0: + version "7.3.0" + resolved "https://registry.npmjs.org/broccoli-babel-transpiler/-/broccoli-babel-transpiler-7.3.0.tgz#a0ad3a37dbf74469664bbca403d652070c2c1317" + integrity sha512-tsXNvDf3gp6g8rGkz234AhbaIRUsCdd6CM3ikfkJVB0EpC8ZAczGsFKTjENLy1etx4s7FkruW/QjI7Wfdhx6Ng== + dependencies: + "@babel/core" "^7.3.3" + "@babel/polyfill" "^7.0.0" + broccoli-funnel "^2.0.2" + broccoli-merge-trees "^3.0.2" + broccoli-persistent-filter "^2.2.1" + clone "^2.1.2" + hash-for-dep "^1.4.7" + heimdalljs-logger "^0.1.9" + json-stable-stringify "^1.0.1" + rsvp "^4.8.4" + workerpool "^3.1.1" + broccoli-builder@^0.18.14: version "0.18.14" resolved "https://registry.yarnpkg.com/broccoli-builder/-/broccoli-builder-0.18.14.tgz#4b79e2f844de11a4e1b816c3f49c6df4776c312d" @@ -3046,6 +3078,14 @@ ember-assign-polyfill@^2.6.0: ember-cli-babel "^6.16.0" ember-cli-version-checker "^2.0.0" +ember-class-based-modifier@^0.10.0: + version "0.10.0" + resolved "https://registry.npmjs.org/ember-class-based-modifier/-/ember-class-based-modifier-0.10.0.tgz#bb40c05440a959d84e9e2f5038e3894773686d19" + integrity sha512-GPPOCYmRibGBUfiuq7oHQAGXgM6X8LK2I2crvQadqwbft5PjXDbR6x1xnOQ8TDWeOE0EluahXXOgWgdUVcmaMQ== + dependencies: + ember-cli-babel "^7.10.0" + ember-modifier-manager-polyfill "^1.2.0" + ember-cli-app-version@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ember-cli-app-version/-/ember-cli-app-version-3.2.0.tgz#7b9ad0e1b63ae0518648356ee24c703e922bc26e" @@ -3105,6 +3145,33 @@ ember-cli-babel@^7.0.0, ember-cli-babel@^7.1.2, ember-cli-babel@^7.4.1, ember-cl ensure-posix-path "^1.0.2" semver "^5.5.0" +ember-cli-babel@^7.10.0: + version "7.12.0" + resolved "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-7.12.0.tgz#064997d199384be8c88d251f30ef67953d3bddc5" + integrity sha512-+EGQsbPvh19nNXHCm6rVBx2CdlxQlzxMyhey5hsGViDPriDI4PFYXYaFWdGizDrmZoDcG/Ywpeph3hl0NxGQTg== + dependencies: + "@babel/core" "^7.0.0" + "@babel/plugin-proposal-class-properties" "^7.3.4" + "@babel/plugin-proposal-decorators" "^7.3.0" + "@babel/plugin-transform-modules-amd" "^7.0.0" + "@babel/plugin-transform-runtime" "^7.2.0" + "@babel/polyfill" "^7.0.0" + "@babel/preset-env" "^7.0.0" + "@babel/runtime" "^7.2.0" + amd-name-resolver "^1.2.1" + babel-plugin-debug-macros "^0.3.0" + babel-plugin-ember-modules-api-polyfill "^2.12.0" + babel-plugin-module-resolver "^3.1.1" + broccoli-babel-transpiler "^7.3.0" + broccoli-debug "^0.6.4" + broccoli-funnel "^2.0.1" + broccoli-source "^1.1.0" + clone "^2.1.2" + ember-cli-babel-plugin-helpers "^1.1.0" + ember-cli-version-checker "^2.1.2" + ensure-posix-path "^1.0.2" + semver "^5.5.0" + ember-cli-broccoli-sane-watcher@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ember-cli-broccoli-sane-watcher/-/ember-cli-broccoli-sane-watcher-3.0.0.tgz#dc1812c047e1ceec4413d3c41b51a9ffc61b4cfe" @@ -3385,7 +3452,7 @@ ember-cli@~3.10.1: watch-detector "^0.1.0" yam "^1.0.0" -ember-compatibility-helpers@^1.1.1, ember-compatibility-helpers@^1.1.2: +ember-compatibility-helpers@^1.1.1, ember-compatibility-helpers@^1.1.2, ember-compatibility-helpers@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/ember-compatibility-helpers/-/ember-compatibility-helpers-1.2.0.tgz#feee16c5e9ef1b1f1e53903b241740ad4b01097e" integrity sha512-pUW4MzJdcaQtwGsErYmitFRs0rlCYBAnunVzlFFUBr4xhjlCjgHJo0b53gFnhTgenNM3d3/NqLarzRhDTjXRTg== @@ -3457,6 +3524,15 @@ ember-maybe-import-regenerator@^0.1.6: ember-cli-babel "^6.0.0-beta.4" regenerator-runtime "^0.9.5" +ember-modifier-manager-polyfill@^1.1.0, ember-modifier-manager-polyfill@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/ember-modifier-manager-polyfill/-/ember-modifier-manager-polyfill-1.2.0.tgz#cf4444e11a42ac84f5c8badd85e635df57565dda" + integrity sha512-bnaKF1LLKMkBNeDoetvIJ4vhwRPKIIumWr6dbVuW6W6p4QV8ZiO+GdF8J7mxDNlog9CeL9Z/7wam4YS86G8BYA== + dependencies: + ember-cli-babel "^7.10.0" + ember-cli-version-checker "^2.1.2" + ember-compatibility-helpers "^1.2.0" + ember-qunit@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/ember-qunit/-/ember-qunit-4.4.1.tgz#3654cadf9fa7e2287fe7b61fc7f19c3eb06222b5" @@ -3483,6 +3559,11 @@ ember-resolver@^5.0.1: ember-cli-version-checker "^3.0.0" resolve "^1.10.0" +ember-rfc176-data@^0.3.12: + version "0.3.12" + resolved "https://registry.npmjs.org/ember-rfc176-data/-/ember-rfc176-data-0.3.12.tgz#90d82878e69e2ac9a5438e8ce14d12c6031c5bd2" + integrity sha512-g9HeZj/gU5bfIIrGXkP7MhS2b3Vu5DfNUrYr14hy99TgIvtZETO+96QF4WOEUXGjIJdfTRjerVnQlqngPQSv1g== + ember-rfc176-data@^0.3.9: version "0.3.9" resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.9.tgz#44b6e051ead6c044ea87bd551f402e2cf89a7e3d"