├── .editorconfig ├── .ember-cli ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .travis.yml ├── .watchmanconfig ├── LICENSE.md ├── README.md ├── addon ├── .gitkeep ├── formatters │ ├── array.js │ ├── index.js │ ├── lazy.js │ ├── object.js │ └── utils │ │ └── jsonml.js └── initializers │ └── install-devtools.js ├── app ├── .gitkeep └── initializers │ └── install-devtools.js ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── package.json ├── testem.js ├── tests ├── .eslintrc.js ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ └── .gitkeep │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── resolver.js │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ └── components │ │ │ └── .gitkeep │ ├── config │ │ ├── environment.js │ │ └── targets.js │ └── public │ │ ├── crossdomain.xml │ │ └── robots.txt ├── helpers │ ├── destroy-app.js │ ├── module-for-acceptance.js │ ├── resolver.js │ └── start-app.js ├── index.html ├── integration │ └── .gitkeep ├── test-helper.js └── unit │ ├── .gitkeep │ ├── formatters │ ├── array-formatter-test.js │ └── object-formatter-test.js │ └── initializers │ └── install-ember-devtools-test.js ├── vendor └── .gitkeep └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 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 21 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 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 | } 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | ecmaVersion: 2017, 5 | sourceType: 'module' 6 | }, 7 | extends: 'eslint:recommended', 8 | env: { 9 | browser: true 10 | }, 11 | rules: { 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | /bower_components 10 | 11 | # misc 12 | /.sass-cache 13 | /connect.lock 14 | /coverage/* 15 | /libpeerconnection.log 16 | npm-debug.log* 17 | testem.log 18 | /.node_modules.ember-try 19 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /bower_components 2 | /config/ember-try.js 3 | /dist 4 | /tests 5 | /tmp 6 | **/.gitkeep 7 | .bowerrc 8 | .editorconfig 9 | .ember-cli 10 | .gitignore 11 | .eslintrc.js 12 | .watchmanconfig 13 | .travis.yml 14 | bower.json 15 | ember-cli-build.js 16 | testem.js 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | - "6" 5 | 6 | sudo: false 7 | 8 | cache: 9 | yarn: true 10 | 11 | env: 12 | # we recommend testing LTS's and latest stable release (bonus points to beta/canary) 13 | - EMBER_TRY_SCENARIO=ember-lts-2.4 14 | - EMBER_TRY_SCENARIO=ember-lts-2.8 15 | - EMBER_TRY_SCENARIO=ember-release 16 | - EMBER_TRY_SCENARIO=ember-beta 17 | - EMBER_TRY_SCENARIO=ember-canary 18 | - EMBER_TRY_SCENARIO=ember-default 19 | 20 | matrix: 21 | fast_finish: true 22 | allow_failures: 23 | - env: EMBER_TRY_SCENARIO=ember-canary 24 | 25 | before_install: 26 | - curl -o- -L https://yarnpkg.com/install.sh | bash 27 | - export PATH=$HOME/.yarn/bin:$PATH 28 | - yarn global add phantomjs-prebuilt 29 | - phantomjs --version 30 | 31 | install: 32 | - yarn install --no-lockfile 33 | 34 | script: 35 | # Usually, it's ok to finish the test scenario without reverting 36 | # to the addon's original dependency state, skipping "cleanup". 37 | - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO test --skip-cleanup 38 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ember-chrome-devtools 2 | 3 | [![npm version](https://badge.fury.io/js/ember-chrome-devtools.svg)](https://www.npmjs.com/package/ember-chrome-devtools) 4 | [![build status](https://travis-ci.org/dwickern/ember-chrome-devtools.svg?branch=master)](https://travis-ci.org/dwickern/ember-chrome-devtools) 5 | 6 | This addon formats Ember objects so that you can see their type and internal state at a glance. No more typing `.get()` into your console! 7 | 8 | x 9 | 10 | 11 | ## Installation 12 | 13 | 1. Install the addon: 14 | 15 | ``` 16 | ember install ember-chrome-devtools 17 | ``` 18 | 19 | (it will not affect your production build) 20 | 21 | 2. Enable custom formatters in DevTools Settings: 22 | 23 | 24 | Enable custom formatters 25 | 26 | 27 | ## Running 28 | 29 | * `ember serve` 30 | * Visit your app at [http://localhost:4200](http://localhost:4200). 31 | 32 | ## Running Tests 33 | 34 | * `npm test` (Runs `ember try:each` to test your addon against multiple Ember versions) 35 | * `ember test` 36 | * `ember test --server` 37 | 38 | ## Building 39 | 40 | * `ember build` 41 | 42 | ## Debugging the debugger 43 | 44 | It can be helpful to inspect the html created by the formatters. Fortunately you can debug DevTools using a second DevTools instance. 45 | 46 | 1. Hit Cmd-Alt-I to open DevTools 47 | 1. Split DevTools in its own window using the first "Dock side" option: 48 | 49 | screen shot 2017-06-30 at 4 30 50 pm 50 | 51 | 1. With DevTools focused, hit Cmd-Alt-I to open a second DevTools 52 | 53 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwickern/ember-chrome-devtools/aa31841b92f802aeba75b858b4283183446e5c84/addon/.gitkeep -------------------------------------------------------------------------------- /addon/formatters/array.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import { properties } from './object'; 3 | import * as jml from './utils/jsonml'; 4 | 5 | export function item(idx, value) { 6 | return jml.item( 7 | jml.name(idx), 8 | jml.separator(), 9 | jml.reference(value) 10 | ); 11 | } 12 | 13 | export function * items(obj) { 14 | for (const [ idx, value ] of obj.toArray().entries()) { 15 | yield item(idx, value); 16 | } 17 | } 18 | 19 | export class ArrayFormatter { 20 | header(obj) { 21 | if (obj instanceof Ember.Object && Ember.Array.detect(obj)) { 22 | const length = Ember.cacheFor(obj, 'length'); 23 | return jml.header(`${obj}(length = ${length || '?'})`); 24 | } 25 | } 26 | hasBody() { 27 | return true; 28 | } 29 | body(obj) { 30 | return jml.list(...items(obj), ...properties(obj)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /addon/formatters/index.js: -------------------------------------------------------------------------------- 1 | import {ObjectFormatter} from './object'; 2 | import {ArrayFormatter} from './array'; 3 | import {LazyFormatter} from './lazy'; 4 | 5 | export default [ 6 | new LazyFormatter, 7 | new ObjectFormatter, 8 | new ArrayFormatter 9 | ]; 10 | -------------------------------------------------------------------------------- /addon/formatters/lazy.js: -------------------------------------------------------------------------------- 1 | 2 | /** Evaluate Ember computed properties and es5 getters when expanded */ 3 | export class Lazy { 4 | constructor(compute) { 5 | this.compute = compute; 6 | } 7 | } 8 | 9 | export class LazyFormatter { 10 | header(obj) { 11 | if (obj instanceof Lazy) { 12 | const style = ` 13 | opacity: 0.8; 14 | font-style: italic; 15 | `; 16 | return ['span', { style }, '(...)'] 17 | } 18 | } 19 | hasBody() { 20 | return true; 21 | } 22 | body(obj) { 23 | return obj.compute(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /addon/formatters/object.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import * as jml from './utils/jsonml'; 3 | 4 | /** All properties from the object and its prototype chain, including non-enumerables */ 5 | function * getProperties(obj) { 6 | const seen = Object.create(null); 7 | 8 | do { 9 | for (const name of Object.getOwnPropertyNames(obj)) { 10 | if (!(name in seen)) { 11 | seen[name] = true; 12 | yield [ name, Object.getOwnPropertyDescriptor(obj, name) ]; 13 | } 14 | } 15 | } while ((obj = Object.getPrototypeOf(obj))); 16 | } 17 | 18 | /** Used in tests */ 19 | function lookupDescriptor(obj, key) { 20 | do { 21 | const descriptor = Object.getOwnPropertyDescriptor(obj, key); 22 | if (descriptor) { 23 | return descriptor; 24 | } 25 | } while ((obj = Object.getPrototypeOf(obj))); 26 | } 27 | 28 | /** 29 | * Compute JsonML for an object property 30 | * @param obj the object 31 | * @param key the property name 32 | * @param descriptor the property descriptor 33 | * @returns {*} JsonML 34 | */ 35 | export function property(obj, key, descriptor = lookupDescriptor(obj, key)) { 36 | Ember.assert('missing descriptor', !!descriptor); 37 | 38 | const enumerable = descriptor.enumerable; 39 | 40 | if (typeof descriptor.get === 'function') { 41 | if (descriptor.get.name === 'GETTER_FUNCTION' || 42 | descriptor.get.isInheritingGetter || 43 | key === 'isDestroyed' || 44 | key === 'isDestroying') { 45 | // Ember getter that's probably safe to evaluate 46 | const value = descriptor.get.call(obj); 47 | return jml.item( 48 | jml.name(key, enumerable), 49 | jml.separator(), 50 | jml.reference(value) 51 | ); 52 | } 53 | 54 | // ES5 getter: forcing the property to compute might have a side effect 55 | return jml.item( 56 | jml.name(key, enumerable), 57 | jml.separator(), 58 | jml.lazy(() => jml.reference(Ember.get(obj, key))) 59 | ); 60 | } 61 | 62 | const value = descriptor.value; 63 | if (typeof value === 'function') { 64 | // ignore 65 | return; 66 | } 67 | 68 | if (value instanceof Ember.ComputedProperty) { 69 | // For convenience, show cached ComputedProperty without needing to expand it 70 | // Don't check the cache for AliasedProperty https://github.com/emberjs/ember.js/issues/15545 71 | const cached = Ember.cacheFor(obj, key); 72 | if (cached !== undefined) { 73 | // use the cached value 74 | return jml.item( 75 | jml.name(key, enumerable), 76 | jml.separator(), 77 | jml.computedPropertyIcon(), 78 | jml.reference(cached) 79 | ); 80 | } 81 | } 82 | 83 | if (value !== null && typeof value === 'object' && value.isDescriptor) { 84 | // ComputedProperty (not cached) or AliasedProperty 85 | // Create a lazy getter, because forcing the property to compute might have a side effect 86 | return jml.item( 87 | jml.name(key, enumerable), 88 | jml.separator(), 89 | jml.computedPropertyIcon(), 90 | jml.lazy(() => jml.reference(Ember.get(obj, key))) 91 | ); 92 | } 93 | 94 | return jml.item( 95 | jml.name(key, enumerable), 96 | jml.separator(), 97 | jml.reference(value) 98 | ); 99 | } 100 | 101 | export function * properties(obj) { 102 | for (const [ key, descriptor ] of getProperties(obj)) { 103 | const element = property(obj, key, descriptor); 104 | if (element) { 105 | yield element; 106 | } 107 | } 108 | } 109 | 110 | export class ObjectFormatter { 111 | header(obj) { 112 | if (obj instanceof Ember.Object && !Ember.Array.detect(obj)) { 113 | return jml.header(obj.toString()); 114 | } 115 | } 116 | hasBody() { 117 | return true; 118 | } 119 | 120 | body(obj) { 121 | return jml.list(...properties(obj)); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /addon/formatters/utils/jsonml.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {Lazy} from '../lazy'; 3 | 4 | export function header(text) { 5 | const style = ` 6 | color: #E14E34; 7 | margin-bottom: 4px; 8 | `; 9 | 10 | return ['div', { style }, emberIcon(), ` ${text}`]; 11 | } 12 | 13 | export function emberIcon() { 14 | const style = ` 15 | background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KTMInWQAAAuVJREFUOBE9U0tIlFEU/u7/mvfoNJqmlYmLnuJGYdwMEppB0YtISqhdEBREj1WLNkG7qF1IUFJt2khGEGEtEnORUCSYmGDMNIWpMzrv/3k79454mPnv45zvO8/LMpeOchIwDoBt/sUeCrg4qzoUhYF7dOk5dMGhqBqdHbilEjQBZIwMiEQwyFXcaQpQLoDnfhAuB67GwRr2QQ3Xk50Hz3bhmRVonBil400SWsgrecimYXQfQaDvLpRgCLxSQmXqA6xvk1AiRKaoZAiKQADFTkRAYC4U+X8IDV1D9OR5CRZqIcGubmQfqzC/fAT3hykQD0oNTFpBJMAby/D3n0X9mWEJdtZWUJycgJtdBQtHYfQk4cxOwbNEPUSlXBfcoQOxwTQp1ALCyUHAFwAcG/lXo8gmB2AtzkuATM+iLSd7cqp5xTwYgd3KBrjrwDjYC31nmzS2UksoPb0N4+oQ9F3t8s5ZnAMaCE/kQjTByHwK/F0JWPMzMA50QQlTfiT2nzT8x68jdvkWtOZWmPOzKL8eAduRIKUpbVhqYA8PX7iJ2MUrcHNZyaw1tUglN6tE7qf0XJSmPyH/7CGc34vggQhE6kI0pgfgFYvSmxbfDhYIUnykoY4wXYf5cw6FsReojN0HC0XgSwzD/puBu5yidhtgmXNJ7lULULc1Qu/oRPDwMQR7+2SBvPU1rN67ASe1AG1/D4L9J+Bra0fuySNUPr8BizRTEUWYGkVRqqA88Rz2wlf49h6CGm+EEo0hfueB7JJaVw9m+ODl1+Hmc+SdUhNjTV8yoFbaNtSWTrjkdf3lCPV9hZqsQI3FoTU2SbCoSf79OE3jW0onJmtDMyt46ENt5aUNKL4oyu9G4WZ+ITR4Gmprm/TqpJdgfp9BZXocLNpBRawNEkufStD78ahm4hEIoT0VlrvUpuoqGZbAKFwOnSaVViO0BRbWGqchEu9BxFH7EZUt6qIDdbupmCLLWq6ebYFbVYHbkv9aQEzuWmoUDAAAAABJRU5ErkJggg==) 16 | no-repeat 17 | left center; 18 | padding-right: 16px; 19 | border-radius: 2px; 20 | `; 21 | 22 | return ['span', { style }, '']; 23 | } 24 | 25 | export function computedPropertyIcon() { 26 | const style = ` 27 | background-color: #E14E34; 28 | color: white; 29 | padding: 0 4px; 30 | margin-right: 6px; 31 | border-radius: 2px; 32 | `; 33 | return ['span', { style }, 'get']; 34 | } 35 | 36 | export function list(...children) { 37 | const style = ` 38 | margin: 0; 39 | z-index: 0; 40 | position: relative; 41 | padding-left: 12px; 42 | display: flex; 43 | flex-direction: column; 44 | `; 45 | return ['ol', { style }, ...children]; 46 | } 47 | 48 | export function item(...children) { 49 | const style = ` 50 | text-overflow: ellipsis; 51 | white-space: nowrap; 52 | position: relative; 53 | display: flex; 54 | align-items: flex-start; 55 | min-height: 16px; 56 | `; 57 | return ['li', { style }, ...children]; 58 | } 59 | 60 | export function name(name, enumerable = true) { 61 | let style = ` 62 | color: rgb(136, 19, 145); 63 | flex-shrink: 0; 64 | `; 65 | 66 | if (!enumerable) { 67 | style += ` 68 | opacity: 0.6; 69 | `; 70 | } 71 | return ['span', { style }, name]; 72 | } 73 | 74 | export function separator() { 75 | const style = ` 76 | flex-shrink: 0; 77 | padding-right: 5px; 78 | `; 79 | return ['span', { style }, ': ']; 80 | } 81 | 82 | export function reference(object) { 83 | if (object === undefined) { 84 | // Special case for `undefined`, otherwise an error will occur: 85 | // > Custom Formatter Failed: Illegal format: obligatory attribute "object" isn't specified 86 | const style = ` 87 | color: rgb(128, 128, 128); 88 | `; 89 | return ['span', { style }, 'undefined']; 90 | } 91 | 92 | if (object !== null && typeof object === 'object' && !(object instanceof Ember.Object)) { 93 | // fix alignment 94 | const style = ` 95 | margin-top: -4px; 96 | `; 97 | 98 | return ['span', { style }, 99 | ['object', { object }] 100 | ]; 101 | } 102 | 103 | return ['object', { object }]; 104 | } 105 | 106 | export function lazy(compute) { 107 | return ['object', { object: new Lazy(compute) }]; 108 | } 109 | -------------------------------------------------------------------------------- /addon/initializers/install-devtools.js: -------------------------------------------------------------------------------- 1 | import formatters from '../formatters'; 2 | 3 | export function initialize(/* application */) { 4 | const old = window.devtoolsFormatters || []; 5 | window.devtoolsFormatters = [ ...old, ...formatters ]; 6 | } 7 | 8 | export default { 9 | name: 'install-devtools', 10 | initialize 11 | }; 12 | -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwickern/ember-chrome-devtools/aa31841b92f802aeba75b858b4283183446e5c84/app/.gitkeep -------------------------------------------------------------------------------- /app/initializers/install-devtools.js: -------------------------------------------------------------------------------- 1 | export { default, initialize } from 'ember-chrome-devtools/initializers/install-devtools'; 2 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | scenarios: [ 4 | { 5 | name: 'ember-lts-2.4', 6 | bower: { 7 | dependencies: { 8 | 'ember': 'components/ember#lts-2-4' 9 | }, 10 | resolutions: { 11 | 'ember': 'lts-2-4' 12 | } 13 | }, 14 | npm: { 15 | devDependencies: { 16 | 'ember-source': null 17 | } 18 | } 19 | }, 20 | { 21 | name: 'ember-lts-2.8', 22 | bower: { 23 | dependencies: { 24 | 'ember': 'components/ember#lts-2-8' 25 | }, 26 | resolutions: { 27 | 'ember': 'lts-2-8' 28 | } 29 | }, 30 | npm: { 31 | devDependencies: { 32 | 'ember-source': null 33 | } 34 | } 35 | }, 36 | { 37 | name: 'ember-release', 38 | bower: { 39 | dependencies: { 40 | 'ember': 'components/ember#release' 41 | }, 42 | resolutions: { 43 | 'ember': 'release' 44 | } 45 | }, 46 | npm: { 47 | devDependencies: { 48 | 'ember-source': null 49 | } 50 | } 51 | }, 52 | { 53 | name: 'ember-beta', 54 | bower: { 55 | dependencies: { 56 | 'ember': 'components/ember#beta' 57 | }, 58 | resolutions: { 59 | 'ember': 'beta' 60 | } 61 | }, 62 | npm: { 63 | devDependencies: { 64 | 'ember-source': null 65 | } 66 | } 67 | }, 68 | { 69 | name: 'ember-canary', 70 | bower: { 71 | dependencies: { 72 | 'ember': 'components/ember#canary' 73 | }, 74 | resolutions: { 75 | 'ember': 'canary' 76 | } 77 | }, 78 | npm: { 79 | devDependencies: { 80 | 'ember-source': null 81 | } 82 | } 83 | }, 84 | { 85 | name: 'ember-default', 86 | npm: { 87 | devDependencies: {} 88 | } 89 | } 90 | ] 91 | }; 92 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | module.exports = function(/* environment, appConfig */) { 5 | return { }; 6 | }; 7 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 3 | 4 | module.exports = function(defaults) { 5 | var app = new EmberAddon(defaults, { 6 | 'ember-cli-babel': { 7 | includePolyfill: true 8 | } 9 | }); 10 | 11 | /* 12 | This build file specifies the options for the dummy test app of this 13 | addon, located in `/tests/dummy` 14 | This build file does *not* influence how the addon or the app using it 15 | behave. You most likely want to be modifying `./index.js` or app's build file 16 | */ 17 | 18 | return app.toTree(); 19 | }; 20 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | name: 'ember-chrome-devtools', 6 | 7 | findHost() { 8 | // `ember-cli` >= 2.7.0 has _findHost 9 | if (typeof this._findHost === 'function') { 10 | return this._findHost(); 11 | } 12 | 13 | // Otherwise we polyfill 14 | let app; 15 | let current = this; 16 | do { 17 | app = current.app || app; 18 | } while (current.parent.parent && (current = current.parent)); 19 | return app; 20 | }, 21 | 22 | treeFor(name) { 23 | let app = this.findHost(); 24 | if (app.env === 'production') { 25 | return; 26 | } 27 | 28 | return this._super.treeFor.apply(this, arguments); 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-chrome-devtools", 3 | "version": "0.2.0", 4 | "description": "Chrome DevTools addon for Ember.js", 5 | "keywords": [ 6 | "ember-addon" 7 | ], 8 | "license": "MIT", 9 | "author": "", 10 | "directories": { 11 | "doc": "doc", 12 | "test": "tests" 13 | }, 14 | "repository": "https://github.com/dwickern/ember-chrome-devtools", 15 | "scripts": { 16 | "build": "ember build", 17 | "start": "ember server", 18 | "test": "ember try:each" 19 | }, 20 | "dependencies": { 21 | "ember-cli-babel": "^6.0.0" 22 | }, 23 | "devDependencies": { 24 | "broccoli-asset-rev": "^2.4.5", 25 | "ember-ajax": "^3.0.0", 26 | "ember-cli": "2.13.2", 27 | "ember-cli-dependency-checker": "^1.3.0", 28 | "ember-cli-eslint": "^3.0.0", 29 | "ember-cli-htmlbars": "^1.1.1", 30 | "ember-cli-htmlbars-inline-precompile": "^0.4.0", 31 | "ember-cli-inject-live-reload": "^1.4.1", 32 | "ember-cli-qunit": "^4.0.0", 33 | "ember-cli-shims": "^1.1.0", 34 | "ember-cli-sri": "^2.1.0", 35 | "ember-cli-uglify": "^1.2.0", 36 | "ember-data": "^2.14.10", 37 | "ember-disable-prototype-extensions": "^1.1.0", 38 | "ember-export-application-global": "^2.0.0", 39 | "ember-load-initializers": "^1.0.0", 40 | "ember-resolver": "^4.0.0", 41 | "ember-source": "~2.13.0", 42 | "ember-welcome-page": "^3.0.0", 43 | "loader.js": "^4.2.3" 44 | }, 45 | "engines": { 46 | "node": ">= 4" 47 | }, 48 | "ember-addon": { 49 | "configPath": "tests/dummy/config" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | "test_page": "tests/index.html?hidepassed", 4 | "disable_watching": true, 5 | "launch_in_ci": [ 6 | "PhantomJS" 7 | ], 8 | "launch_in_dev": [ 9 | "PhantomJS", 10 | "Chrome" 11 | ] 12 | }; 13 | -------------------------------------------------------------------------------- /tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | embertest: true 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Resolver from './resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | let App; 7 | 8 | Ember.MODEL_FACTORY_INJECTIONS = true; 9 | 10 | App = Ember.Application.extend({ 11 | modulePrefix: config.modulePrefix, 12 | podModulePrefix: config.podModulePrefix, 13 | Resolver 14 | }); 15 | 16 | loadInitializers(App, config.modulePrefix); 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwickern/ember-chrome-devtools/aa31841b92f802aeba75b858b4283183446e5c84/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwickern/ember-chrome-devtools/aa31841b92f802aeba75b858b4283183446e5c84/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwickern/ember-chrome-devtools/aa31841b92f802aeba75b858b4283183446e5c84/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwickern/ember-chrome-devtools/aa31841b92f802aeba75b858b4283183446e5c84/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | 3 | export default Resolver; 4 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import config from './config/environment'; 3 | 4 | const Router = Ember.Router.extend({ 5 | location: config.locationType, 6 | rootURL: config.rootURL 7 | }); 8 | 9 | Router.map(function() { 10 | }); 11 | 12 | export default Router; 13 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwickern/ember-chrome-devtools/aa31841b92f802aeba75b858b4283183446e5c84/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwickern/ember-chrome-devtools/aa31841b92f802aeba75b858b4283183446e5c84/tests/dummy/app/styles/app.css -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{!-- The following component displays Ember's default welcome message. --}} 2 | {{welcome-page}} 3 | {{!-- Feel free to remove this! --}} 4 | 5 | {{outlet}} -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwickern/ember-chrome-devtools/aa31841b92f802aeba75b858b4283183446e5c84/tests/dummy/app/templates/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = function(environment) { 4 | var ENV = { 5 | modulePrefix: 'dummy', 6 | environment: environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. 'with-controller': true 13 | }, 14 | EXTEND_PROTOTYPES: { 15 | // Prevent Ember Data from overriding Date.parse. 16 | Date: false 17 | } 18 | }, 19 | 20 | APP: { 21 | // Here you can pass flags/options to your application instance 22 | // when it is created 23 | } 24 | }; 25 | 26 | if (environment === 'development') { 27 | // ENV.APP.LOG_RESOLVER = true; 28 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 29 | // ENV.APP.LOG_TRANSITIONS = true; 30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 31 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 32 | } 33 | 34 | if (environment === 'test') { 35 | // Testem prefers this... 36 | ENV.locationType = 'none'; 37 | 38 | // keep test console output quieter 39 | ENV.APP.LOG_ACTIVE_GENERATION = false; 40 | ENV.APP.LOG_VIEW_LOOKUPS = false; 41 | 42 | ENV.APP.rootElement = '#ember-testing'; 43 | } 44 | 45 | if (environment === 'production') { 46 | 47 | } 48 | 49 | return ENV; 50 | }; 51 | -------------------------------------------------------------------------------- /tests/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = { 4 | browsers: [ 5 | 'ie 9', 6 | 'last 1 Chrome versions', 7 | 'last 1 Firefox versions', 8 | 'last 1 Safari versions' 9 | ] 10 | }; 11 | -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/helpers/destroy-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default function destroyApp(application) { 4 | Ember.run(application, 'destroy'); 5 | } 6 | -------------------------------------------------------------------------------- /tests/helpers/module-for-acceptance.js: -------------------------------------------------------------------------------- 1 | import { module } from 'qunit'; 2 | import Ember from 'ember'; 3 | import startApp from '../helpers/start-app'; 4 | import destroyApp from '../helpers/destroy-app'; 5 | 6 | const { RSVP: { Promise } } = Ember; 7 | 8 | export default function(name, options = {}) { 9 | module(name, { 10 | beforeEach() { 11 | this.application = startApp(); 12 | 13 | if (options.beforeEach) { 14 | return options.beforeEach.apply(this, arguments); 15 | } 16 | }, 17 | 18 | afterEach() { 19 | let afterEach = options.afterEach && options.afterEach.apply(this, arguments); 20 | return Promise.resolve(afterEach).then(() => destroyApp(this.application)); 21 | } 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /tests/helpers/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from '../../resolver'; 2 | import config from '../../config/environment'; 3 | 4 | const resolver = Resolver.create(); 5 | 6 | resolver.namespace = { 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix 9 | }; 10 | 11 | export default resolver; 12 | -------------------------------------------------------------------------------- /tests/helpers/start-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Application from '../../app'; 3 | import config from '../../config/environment'; 4 | 5 | export default function startApp(attrs) { 6 | let attributes = Ember.merge({}, config.APP); 7 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override; 8 | 9 | return Ember.run(() => { 10 | let application = Application.create(attributes); 11 | application.setupForTesting(); 12 | application.injectTestHelpers(); 13 | return application; 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{content-for "body-footer"}} 31 | {{content-for "test-body-footer"}} 32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwickern/ember-chrome-devtools/aa31841b92f802aeba75b858b4283183446e5c84/tests/integration/.gitkeep -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import resolver from './helpers/resolver'; 2 | import { 3 | setResolver 4 | } from 'ember-qunit'; 5 | import { start } from 'ember-cli-qunit'; 6 | 7 | setResolver(resolver); 8 | start(); 9 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwickern/ember-chrome-devtools/aa31841b92f802aeba75b858b4283183446e5c84/tests/unit/.gitkeep -------------------------------------------------------------------------------- /tests/unit/formatters/array-formatter-test.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import DS from 'ember-data'; 3 | import QUnit, { module, test } from 'qunit'; 4 | import { property } from 'ember-chrome-devtools/formatters/object'; 5 | import { ArrayFormatter, item } from 'ember-chrome-devtools/formatters/array'; 6 | 7 | module('Unit | Formatters | array formatter'); 8 | 9 | const formatter = new ArrayFormatter(); 10 | 11 | test('used for Ember.ArrayProxy only', function(assert) { 12 | assert.ok(formatter.header(Ember.ArrayProxy.create())); 13 | assert.notOk(formatter.header(Ember.Object.create())); 14 | assert.notOk(formatter.header({})); 15 | assert.notOk(formatter.header([])); 16 | }); 17 | 18 | test('display object properties of promise arrays', function(assert) { 19 | const array = DS.PromiseManyArray.create({ 20 | content: [ 'first', 'second', 'third' ] 21 | }); 22 | const formatted = formatter.body(array); 23 | 24 | let firstItem = item(0, 'first'); 25 | assert.ok(formatted.find(prop => QUnit.equiv(prop, firstItem))); 26 | 27 | let isFulfilled = property(array, 'isFulfilled'); 28 | assert.ok(formatted.find(prop => QUnit.equiv(prop, isFulfilled))); 29 | }); 30 | -------------------------------------------------------------------------------- /tests/unit/formatters/object-formatter-test.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import { module, test } from 'qunit'; 3 | import { ObjectFormatter, property } from 'ember-chrome-devtools/formatters/object'; 4 | import { Lazy } from 'ember-chrome-devtools/formatters/lazy'; 5 | import * as jml from 'ember-chrome-devtools/formatters/utils/jsonml'; 6 | 7 | module('Unit | Formatters | object formatter'); 8 | 9 | function lazyReference(property) { 10 | const ref = property[property.length - 1]; 11 | if (ref.length === 2 && ref[0] === 'object') { 12 | const lazy = ref[1].object; 13 | if (lazy instanceof Lazy) { 14 | return lazy; 15 | } 16 | } 17 | } 18 | 19 | test('used for Ember.Object only', function(assert) { 20 | const formatter = new ObjectFormatter(); 21 | 22 | assert.ok(formatter.header(Ember.Object.create())); 23 | assert.notOk(formatter.header({})); 24 | assert.notOk(formatter.header(Ember.ArrayProxy.create())); 25 | }); 26 | 27 | test('regular property', function(assert) { 28 | const obj = Ember.Object.create({ 29 | foo: 'bar' 30 | }); 31 | 32 | const actual = property(obj, 'foo'); 33 | const expected = jml.item( 34 | jml.name('foo'), 35 | jml.separator(), 36 | jml.reference('bar') 37 | ); 38 | 39 | assert.deepEqual(actual, expected); 40 | }); 41 | 42 | test('lazy evaluate computed property', function(assert) { 43 | let hadSideEffect = false; 44 | 45 | const MyClass = Ember.Object.extend({ 46 | foo: Ember.computed(function() { 47 | hadSideEffect = true; 48 | return 'bar'; 49 | }) 50 | }); 51 | const obj = MyClass.create(); 52 | 53 | const actual = property(obj, 'foo'); 54 | assert.ok(lazyReference(actual)); 55 | assert.notOk(hadSideEffect); 56 | 57 | obj.get('foo'); 58 | assert.ok(hadSideEffect); 59 | }); 60 | 61 | test('show computed property after .get', function(assert) { 62 | const MyClass = Ember.Object.extend({ 63 | foo: Ember.computed(() => 'bar') 64 | }); 65 | const obj = MyClass.create(); 66 | obj.get('foo'); // force evaluation 67 | 68 | const actual = property(obj, 'foo'); 69 | const expected = jml.item( 70 | jml.name('foo'), 71 | jml.separator(), 72 | jml.computedPropertyIcon(), 73 | jml.reference('bar') 74 | ); 75 | 76 | assert.deepEqual(actual, expected); 77 | }); 78 | 79 | test('show cached property after .set', function(assert) { 80 | const obj = Ember.Object.create(); 81 | obj.set('foo', 'bar'); 82 | 83 | const actual = property(obj, 'foo'); 84 | const expected = jml.item( 85 | jml.name('foo'), 86 | jml.separator(), 87 | jml.reference('bar') 88 | ); 89 | 90 | assert.deepEqual(actual, expected); 91 | }); 92 | 93 | test('show isDestroyed', function(assert) { 94 | const obj = Ember.Object.create(); 95 | 96 | // Ember.Object#isDestroyed is enumerable on some ember versions but not others 97 | // we don't really care either way but it will cause the deep equality check to fail 98 | const descriptor = Object.getOwnPropertyDescriptor(Ember.Object.prototype, 'isDestroyed'); 99 | 100 | const actual = property(obj, 'isDestroyed'); 101 | const expected = jml.item( 102 | jml.name('isDestroyed', descriptor.enumerable), 103 | jml.separator(), 104 | jml.reference(false) 105 | ); 106 | 107 | assert.deepEqual(actual, expected); 108 | }); 109 | 110 | test('AliasedProperty', function(assert) { 111 | const MyClass = Ember.Object.extend({ 112 | foo: Ember.computed(() => 'bar'), 113 | bar: Ember.computed.readOnly('foo') 114 | }); 115 | const obj = MyClass.create(); 116 | 117 | const notCached = lazyReference(property(obj, 'bar')); 118 | assert.ok(notCached); 119 | 120 | obj.get('bar'); // now cached 121 | 122 | const cached = lazyReference(property(obj, 'bar')); 123 | assert.ok(cached); 124 | 125 | const expected = jml.reference('bar'); 126 | assert.deepEqual(notCached.compute(), expected); 127 | assert.deepEqual(cached.compute(), expected); 128 | }); 129 | -------------------------------------------------------------------------------- /tests/unit/initializers/install-ember-devtools-test.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import { initialize } from 'dummy/initializers/install-devtools'; 3 | import { module, test } from 'qunit'; 4 | import destroyApp from '../../helpers/destroy-app'; 5 | 6 | module('Unit | Initializer | install ember devtools', { 7 | beforeEach() { 8 | Ember.run(() => { 9 | this.application = Ember.Application.create(); 10 | this.application.deferReadiness(); 11 | }); 12 | }, 13 | afterEach() { 14 | destroyApp(this.application); 15 | } 16 | }); 17 | 18 | test('adds ember formatters', function(assert) { 19 | window.devtoolsFormatters = null; 20 | 21 | initialize(this.application); 22 | 23 | assert.ok(window.devtoolsFormatters instanceof Array && window.devtoolsFormatters.length > 0, 'add ember formatters'); 24 | }); 25 | 26 | test('keeps existing formatters', function(assert) { 27 | const formatter = {}; 28 | window.devtoolsFormatters = [ formatter ]; 29 | 30 | initialize(this.application); 31 | 32 | assert.ok(window.devtoolsFormatters instanceof Array && window.devtoolsFormatters.length > 0, 'add ember formatters'); 33 | assert.ok(window.devtoolsFormatters.indexOf(formatter) !== -1, 'keep the existing formatter'); 34 | }); 35 | -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwickern/ember-chrome-devtools/aa31841b92f802aeba75b858b4283183446e5c84/vendor/.gitkeep --------------------------------------------------------------------------------