|
| 1 | +# Ember ES6 - How to run ES6 class codemods |
| 2 | + |
| 3 | +## Overview |
| 4 | +The [ES6 native classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) support is available in Ember starting with 3.4.x via [polyfill](https://github.com/pzuraq/ember-native-class-polyfill) (3.6.x without polyfill). Transitioning to the new ES6 classes syntax would provide better overall developer experience. The [ember ES6 class codemods](https://github.com/scalvert/ember-es6-class-codemod) are developed to make this transition easier. |
| 5 | + |
| 6 | +The purpose of this document is to provide step by step guide to install and run codemods on your code base. |
| 7 | + |
| 8 | +## Prerequisites |
| 9 | +The application must be running on |
| 10 | +- `ember-source@^3.4.6` |
| 11 | +- `ember-cli-babel@^7.x.x` |
| 12 | + |
| 13 | +## Dependencies |
| 14 | + |
| 15 | +Before moving on to installation steps, let's take a quick overview of dependencies which will be added to the application. |
| 16 | + |
| 17 | +### [ember-es6-class-codemod](https://github.com/scalvert/ember-es6-class-codemod) |
| 18 | +Codemods for transforming ember app code to native ES6 class syntax with decorators. The codemods can be installed globally (recommended) or locally. |
| 19 | + |
| 20 | +### [ember-es6-class-codemod-dyfactor](https://github.com/ssutar/ember-es6-class-codemod-dyfactor) |
| 21 | +[Dyfactor](https://github.com/dyfactor/dyfactor) is a plugin runner system which allows you to collect the runtime information about the code. The [ember-es6-class-codemod-dyfactor](https://github.com/ssutar/ember-es6-class-codemod-dyfactor) is a dyfactor plugin to extract the runtime data about ember objects. This data need to be passed to the codemods to provide metadata about the objects being transformed. |
| 22 | + |
| 23 | +### [eslint-plugin-ember-es6-class](https://github.com/scalvert/eslint-plugin-ember-es6-class) |
| 24 | +Eslint plugin for enforcing usages of Ember ES6 classes in your application. Recommended to enable after transformation to ES6 classes to prevent addition of code in EmberObject.extend syntax. |
| 25 | + |
| 26 | +### [ember-decorators](https://github.com/ember-decorators/ember-decorators) |
| 27 | +Ember decorators provide a set of [decorators](https://github.com/tc39/proposal-decorators#decorators) which can be used to write native classes with every standard feature that is available in Ember, along with the transforms and build system required to polyfill |
| 28 | + |
| 29 | +### [ember-native-class-polyfill](https://github.com/pzuraq/ember-native-class-polyfill) |
| 30 | +This addon provides a polyfill for the native class behavior that was proposed in Ember RFCs [#240](https://emberjs.github.io/rfcs/0240-es-classes.html) and [#337](https://emberjs.github.io/rfcs/0337-native-class-constructor-update.html). |
| 31 | + |
| 32 | +## Installation |
| 33 | +Install [ember-es6-class-codemod](https://github.com/scalvert/ember-es6-class-codemod) globally |
| 34 | +``` |
| 35 | +yarn global add ember-es6-class-codemod |
| 36 | +``` |
| 37 | +Install the following dev dependencies in your project: |
| 38 | +- [eslint rule](https://github.com/scalvert/eslint-plugin-ember-es6-class) and |
| 39 | +- [dyfactor plugin](https://github.com/ssutar/ember-es6-class-codemod-dyfactor) |
| 40 | +``` |
| 41 | +yarn add eslint-plugin-ember-es6-class ember-es6-class-codemod-dyfactor --dev |
| 42 | +``` |
| 43 | +- Install the [ember decorators](https://github.com/ember-decorators/ember-decorators) in your project: |
| 44 | + |
| 45 | +``` |
| 46 | +yarn ember install ember-decorators |
| 47 | +``` |
| 48 | +- _Install [ember native class polyfill](https://github.com/pzuraq/ember-native-class-polyfill) only if your application is running on `ember-source` less than v3.6.x_ |
| 49 | +``` |
| 50 | +yarn ember install ember-native-class-polyfill |
| 51 | +``` |
| 52 | + |
| 53 | +## Setup dyfactor plugin |
| 54 | +Once all the dependencies are installed successfully, the application need to be setup to run the [dyfactor plugin](https://github.com/ssutar/ember-es6-class-codemod-dyfactor), which is used to gather valuable runtime information from the application for use within the codemod |
| 55 | + |
| 56 | +To initialize the dyfactor plugin, run |
| 57 | +``` |
| 58 | +yarn dyfactor init |
| 59 | +``` |
| 60 | +A file `.dyfactor.json` will be created in the current directory. |
| 61 | + |
| 62 | +To view the list of dyfactor plugins installed |
| 63 | +``` |
| 64 | +yarn dyfactor list-plugins |
| 65 | +
|
| 66 | +The list of available dyfactor plugins will be displayed, for example: |
| 67 | +
|
| 68 | +Plugins |
| 69 | +============= |
| 70 | +Name: ember-object Type: template Levels: extract, modify |
| 71 | +✨ Done in 0.83s. |
| 72 | +``` |
| 73 | + |
| 74 | +Open `.dyfactor.json` and set the entry in navigation.pages list. The page entry must be of the test page url, for example: |
| 75 | +``` |
| 76 | +{ |
| 77 | + "navigation": { |
| 78 | + "pages": [ |
| 79 | + "http://localhost:4200/tests/index.html?runtimedata" |
| 80 | + ] |
| 81 | + } |
| 82 | +} |
| 83 | +``` |
| 84 | +**Note** The dyfactor plugin does not need to run all the tests. It is recommended to configure the url using filters or module/test ids such that it would run a small subset of tests or a single test (preferred). |
| 85 | + |
| 86 | +Edit the `test-helper.js` file from the application and add the following code: |
| 87 | +``` |
| 88 | + // ... Other imports |
| 89 | +
|
| 90 | + import { extract } from "ember-es6-class-codemod-dyfactor/test-support/ember-object"; |
| 91 | +
|
| 92 | + // Add this after all the assets are loaded, just before `start` |
| 93 | +
|
| 94 | + if (QUnit.urlParams.runtimedata) { |
| 95 | + extract(); |
| 96 | + } |
| 97 | +
|
| 98 | + start(); |
| 99 | +``` |
| 100 | +**Note** Make sure to wrap the extract call in some query parameter, and pass in the same query parameter to the configuration url in `.dyfactor.json` |
| 101 | + |
| 102 | +**IMPORTANT** Commit all the changes locally. The dyfactor plugin switches to a new branch and modifies the code. It removes all the uncommitted local changes in the process. |
| 103 | + |
| 104 | +## Collecting runtime data |
| 105 | +Start your application |
| 106 | +``` |
| 107 | +ember serve |
| 108 | +``` |
| 109 | +Run the dyfactor plugin using following command: |
| 110 | +``` |
| 111 | +yarn dyfactor run template ember-object <path> --level extract |
| 112 | +``` |
| 113 | +`<path>` can be any directory in the application for which the runtime data need to be extracted. |
| 114 | + |
| 115 | +This command prompts for user input in different stages of execution |
| 116 | + |
| 117 | +The first prompt is |
| 118 | +``` |
| 119 | +? Start your dev server and press enter to continue... (Continue) |
| 120 | +``` |
| 121 | +Make sure your application is running and press enter. |
| 122 | + |
| 123 | +Once you press enter the dyfactor will |
| 124 | +- Create a new branch |
| 125 | +- Switch to the newly created branch |
| 126 | +- Run the dyfactor plugin codemods on your application code |
| 127 | + |
| 128 | +The next prompt will be |
| 129 | +``` |
| 130 | +? Press enter when your dev server is reset... (Continue) |
| 131 | +``` |
| 132 | +Wait till the server is reset after applying the changes made by dyfactor plugin in the application code. Press enter when server is done reset. |
| 133 | + |
| 134 | +A message will be displayed something like: |
| 135 | +``` |
| 136 | +Collecting telemetry data… |
| 137 | +``` |
| 138 | +At this step a new browser window (chromium) is opened with the url configured in the `.dyfactor.json`. The test will be run and the window will be closed automatically. |
| 139 | + |
| 140 | +After this step the runtime data will be collected in the file `dyfactor-telemetry.json` |
| 141 | + |
| 142 | +## Running codemods |
| 143 | +Run the transforms [ember-object](https://github.com/scalvert/ember-es6-class-codemod/tree/master/transforms/ember-object) |
| 144 | +``` |
| 145 | +ember-es6-class-codemod ember-object <path-to-run-codemods-on> --decorators=true --runtime-config-path=dyfactor-telemetry.json |
| 146 | +``` |
| 147 | + |
| 148 | +The codemods can be run targeting a small subset of application code. For example you can target a single in-repo addon or single type (for example services, controllers etc) in the addon. See the [usage details](https://github.com/scalvert/ember-es6-class-codemod#usage) for all the options. |
| 149 | + |
| 150 | +## Configuring ESLint rule |
| 151 | + |
| 152 | +To enable the [ESLint rule](https://github.com/scalvert/eslint-plugin-ember-es6-class) which will disallow usage of the old `EmberObject.extend` syntax - |
| 153 | + |
| 154 | +Add the following code to `.eslintrc.js` |
| 155 | + |
| 156 | +``` |
| 157 | +module.exports = { |
| 158 | + root: true, |
| 159 | + plugins: ['ember-es6-class'], |
| 160 | +
|
| 161 | + // ... other config ... |
| 162 | +
|
| 163 | + rules: { |
| 164 | + overrides: [{ |
| 165 | + files: ['<transformed-addon-path>/**/*.js'], |
| 166 | + rules: { |
| 167 | + 'ember-es6-class/no-object-extend': 'error', |
| 168 | + }, |
| 169 | + }] |
| 170 | + } |
| 171 | +} |
| 172 | +``` |
| 173 | + |
| 174 | +## Debugging |
| 175 | +Check the `codemods.log` in the directory from where the codemods are being executed. |
| 176 | + |
| 177 | +The codemods execution details are logged into the `codemods.log` file. Specifically, details such as failures and reasons for failures, are logged. This would be the recommended starting point for debugging issues. |
| 178 | + |
| 179 | +## Known errors |
| 180 | +- In dyfactor plugin execution, an error might be logged to console |
| 181 | + ``` |
| 182 | + Error occurred in instrumenting <some/file/path.js> { TypeError: unknown: Property init of VariableDeclarator expected node to be of a type ["Expression"] but instead got "FunctionDeclaration" |
| 183 | + ``` |
| 184 | + |
| 185 | + This means that the file which the plugin is instrumenting does not have a default export, in other words the file does not need transformation. Please ignore this error |
| 186 | + |
| 187 | +- Codemods might throw below error |
| 188 | + ``` |
| 189 | + ERR <path> Transformation error |
| 190 | + <error reason with stack trace> |
| 191 | + ``` |
| 192 | + |
| 193 | + Verify the `<path>` value, in most cases it would be a non js file. While this is [known issue](https://github.com/scalvert/ember-es6-class-codemod/issues/42) in the codemods, it will be fixed soon |
| 194 | + |
| 195 | +- An eslint error might be reported after running codemods. |
| 196 | + ``` |
| 197 | + Parsing error: Using the export keyword between a decorator and a class is not allowed. Please use `export @dec class` instead. |
| 198 | + ``` |
| 199 | + This happens because your application is running on `babel-eslint` v9 or higher. To fix this issue set the `legacyDecorators` option in the `.eslintrc.js` file as following: |
| 200 | + ``` |
| 201 | + module.exports = { |
| 202 | + // ... config options ... |
| 203 | + parserOptions: { |
| 204 | + ecmaFeatures: { legacyDecorators: true }, |
| 205 | + }, |
| 206 | +
|
| 207 | + // ... more config options ... |
| 208 | + } |
| 209 | + ``` |
| 210 | + |
| 211 | +## References |
| 212 | +- [jscodeshift](https://github.com/facebook/jscodeshift) |
| 213 | +- [Recast](https://github.com/benjamn/recast) |
| 214 | +- [ASTExplorer](https://astexplorer.net/) |
| 215 | +- [Codemod-cli](https://github.com/rwjblue/codemod-cli) |
| 216 | +- [Dyfactor](https://github.com/dyfactor/dyfactor) |
| 217 | +- [RFC - Ember native class roadmap](https://github.com/pzuraq/emberjs-rfcs/blob/b47e7f9ec4f02c7d27d50de64691130e7d22747d/text/0000-native-class-roadmap.md) |
| 218 | +- [RFC - Ember native class constructor update](https://github.com/pzuraq/emberjs-rfcs/blob/94b38d429eb2964fa86cd13bea6823a01b3ef68d/text/0000-native-class-constructor-update.md) |
| 219 | +- [RFC - Ember native classes codemods](https://docs.google.com/document/d/18QW1SJ6crN5Lh2ZhsSJgjx-oxpMZXUhYBSnrBqxKprI/edit#heading=h.hmogsghmufas) |
| 220 | +- [ember-es6-class-codemod-dyfactor](https://github.com/ssutar/ember-es6-class-codemod-dyfactor) |
0 commit comments