├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── bin └── ember-cli-migrator ├── lib ├── ember-migrator.js ├── helper-visitor.js ├── migrator-visitor.js └── typed-export.js ├── package-lock.json ├── package.json └── test ├── common_js-debug-test.js ├── common_js-test.js ├── custom-app-name-test.js ├── ember-rails-test.js ├── fixtures ├── common_js │ ├── input │ │ ├── application.js │ │ └── controllers │ │ │ ├── comment_activity.js │ │ │ ├── preserve_comments.js │ │ │ └── with_mixin.js │ └── output │ │ ├── application.js │ │ └── controllers │ │ ├── comment-activity.js │ │ ├── preserve-comments.js │ │ └── with-mixin.js ├── custom_app_name │ ├── input │ │ └── models │ │ │ └── comment_activity.js │ └── output │ │ └── models │ │ └── comment-activity.js ├── ember-rails │ ├── input │ │ └── application.js │ └── output │ │ └── application.js ├── helpers │ ├── input │ │ └── app │ │ │ └── helpers │ │ │ └── reprint.js │ └── output │ │ └── app │ │ └── helpers │ │ └── reprint.js └── vanilla │ ├── input │ ├── application.js │ ├── components │ │ └── kiwi-phone.js │ ├── controllers │ │ ├── comment_activity.js │ │ ├── preserve_comments.js │ │ └── with_mixin.js │ ├── mixins │ │ ├── coffee_mixin.js.coffee │ │ └── useful.js │ ├── models │ │ ├── comment_activity.js │ │ ├── comment_activity_should_ignore.js.erb │ │ ├── comment_activity_with_ds.js │ │ ├── comment_activity_with_em.js │ │ ├── comment_activity_with_path_for_type.js │ │ ├── extended_comment_activity.js │ │ ├── no_import.js │ │ └── user_model_with_serializer.js │ ├── router.js │ ├── routes │ │ ├── index_route.js │ │ └── one_and_two_route.js │ ├── serializers │ │ └── comment_activity.js │ ├── services │ │ └── seattle-alert.js │ ├── store.js │ ├── templates │ │ ├── atemplate.handlebars │ │ └── components │ │ │ └── anothertemplate.hbs │ ├── transforms │ │ └── objecttransform.js │ ├── unknown_type │ │ ├── known_type.js │ │ ├── misc.js │ │ └── misc_long_name.js │ └── views │ │ ├── comment_activity.js │ │ ├── duplicate_name.js │ │ ├── reopen_view.js │ │ ├── should_be_in_templates.handlebars │ │ └── use_duplicates.js │ └── output │ ├── adapters │ └── application.js │ ├── application.js │ ├── components │ └── kiwi-phone.js │ ├── controllers │ ├── comment-activity.js │ ├── preserve-comments.js │ └── with-mixin.js │ ├── mixins │ ├── known-type.js │ └── useful.js │ ├── models │ ├── comment-activity-with-ds.js │ ├── comment-activity-with-em.js │ ├── comment-activity-with-path-for-type.js │ ├── comment-activity.js │ ├── extended-comment-activity.js │ ├── no-import.js │ └── user.js │ ├── nonjs │ ├── mixins │ │ └── coffee-mixin.js.coffee │ └── models │ │ └── comment-activity-should-ignore.js.erb │ ├── router.js │ ├── routes │ ├── index.js │ ├── one.js │ └── two.js │ ├── serializers │ ├── comment-activity.js │ └── user.js │ ├── services │ └── seattle-alert.js │ ├── templates │ ├── atemplate.handlebars │ ├── components │ │ └── anothertemplate.hbs │ └── views │ │ └── should-be-in-templates.handlebars │ ├── transforms │ └── object.js │ ├── unknown-type │ ├── misc-long-name.js │ └── misc.js │ └── views │ ├── comment-activity.js │ ├── duplicate-name-x.js │ ├── duplicate-name.js │ ├── reopen.js │ ├── some-unknown-type.js │ └── use-duplicates.js ├── mocha.opts ├── models-test.js └── test_helper.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | *.swp 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # Compiled binary addons (http://nodejs.org/api/addons.html) 21 | build/Release 22 | 23 | # Dependency directory 24 | # Commenting this out is preferred by some people, see 25 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 26 | node_modules 27 | 28 | # Users Environment Variables 29 | .lock-wscript 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "0.12" 5 | - "iojs" 6 | - "4.2" 7 | - "5" 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Stanley Stuart 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ember-cli-migrator[![Build Status](https://travis-ci.org/fivetanley/ember-cli-migrator.svg?branch=master)](https://travis-ci.org/fivetanley/ember-cli-migrator) 2 | ================== 3 | 4 | **Important: As of 0.5.0 Node JS v0.10 is no longer supported. Please 5 | use Node v0.12 or IOJS instead.** 6 | 7 | # Installation 8 | 9 | `npm install -g ember-cli-migrator` 10 | 11 | # About 12 | 13 | Migrate your files to the standard ember-cli structure, preserving git history. 14 | 15 | You can run the command line tool by running the ember-cli-migrator script from within your existing ember project. 16 | 17 | The goal of the project is to convert global variables to ES6 Modules. For example: 18 | 19 | ```javascript 20 | App.Post = DS.Model.extend({ 21 | 22 | }); 23 | ``` 24 | becomes 25 | 26 | ```javascript 27 | import DS from "ember-data"; 28 | 29 | var Post = DS.Model.extend({ 30 | 31 | }); 32 | 33 | export default Post; 34 | ``` 35 | # Features 36 | - The following known module types are currently handled: 37 | - Components 38 | - Controllers 39 | - Routes 40 | - Views 41 | - Models 42 | - Mixins 43 | - Transforms 44 | - Adapters 45 | - Serializers 46 | - Services 47 | - Converts file names for you and puts them into the canonical ember CLI folders. 48 | - Multiple modules in your current file will be split into their own export module and potentially into different folders. For example if you had a model and serializer defined in the same file, the migrator would split the serializer into a file in the serializers folder and the model module would exist in the models folder. 49 | - Unknown types, e.g., a utility function defined on your app namespace, is exported in a module that is left in the location it was found. 50 | - Non JS files, e.g., handlebars, erb, are moved to a nonJS folder with the same subdirectory hierarchy so they are easy for you to isolate and handle manually. 51 | 52 | # Command Line Options 53 | 54 | ``` 55 | -h, --help output usage information 56 | -V, --version output the version number 57 | -g, --global [name] Global namespace of Ember application, eg: "MyApplication = Ember.Application.." 58 | -a, --ember-cli-app-name [name], Name of application namespace/modulePrefix. This is the name of the app you would pass to `ember new ` 59 | -s, --source [source_directory] Directory to perform migration on 60 | -t, --target [target_directory] Directory to output result of migration 61 | -f, --force Migrate even if output files exist 62 | --keep-source Keep source files. (Target does not replicate git history.) 63 | --ignore-subdirs [comma_separated_dirs] Sub-directories in source to ignore 64 | ``` 65 | 66 | # Example 67 | 68 | To convert an Ember App Kit-like project and put it back in the same parent directory, you can use the following 69 | 70 | `ember-cli-migrator -g App -t . -s . -a appkit` 71 | 72 | # Testing 73 | 74 | You can run the tests by running `npm test` in the root folder. 75 | 76 | # Running the CLI locally 77 | 78 | You can run `bin/ember-cli-migrator` from the root of this project and use the command line arguments above. 79 | 80 | # Dependencies 81 | The project uses [recast](https://github.com/benjamn/recast) (which uses Esprima) to walk the JavaScript AST to accurately identify exports and move the file. 82 | 83 | # Necessary Manual Steps 84 | - App.Router = Ember.Router.extend(); placed at beginning of router file 85 | - import ./config/environment in router 86 | - move injectors & registers to initializers rather than on App 87 | - have to merge app.js code, imports etc. 88 | - move ENV stuff to config/environments.js 89 | - var ObjectTransform = var ArrayTransform = Ember.Transform.extend 90 | 91 | # TODOS 92 | - [ ] helpers 93 | - [ ] Smarter Router migrations 94 | - [ ] Handleabars 95 | -------------------------------------------------------------------------------- /bin/ember-cli-migrator: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var program = require('commander'); 8 | var EmberMigrator = require('../lib/ember-migrator'); 9 | var path = require('path'); 10 | var package = require('../package') 11 | 12 | program 13 | .version(package.version) 14 | .option('-g, --global [name]', 'Global namespace of Ember application, eg: "MyApplication = Ember.Application.."') 15 | .option('-a, --ember-cli-app-name [name]', 'Name of application namespace/modulePrefix. This is the name of the app you would pass to `ember new `') 16 | .option('-s, --source [source_directory]', 'Directory to perform migration on') 17 | .option('-t, --target [target_directory]', 'Directory to output result of migration') 18 | .option('-f, --force', 'Migrate even if output files exist') 19 | .option('--keep-source', 'Keep source files. (Target does not replicate git history.)') 20 | .option('--ignore-subdirs [comma_separated_dirs]', 'Sub-directories in source to ignore') 21 | .parse(process.argv); 22 | 23 | var curDir = './'; 24 | var tmpDir = path.join(curDir, "/tmp"); 25 | 26 | var migrator = new EmberMigrator({ 27 | inputDirectory: program.source || curDir, 28 | outputDirectory: program.target || tmpDir, 29 | forceOutput: program.force, 30 | keepSource: program.keepSource, 31 | ignoreDirs: program.ignoreSubdirs, 32 | appName: program.emberCliAppName, 33 | rootAppName: program.global 34 | }); 35 | 36 | migrator.run(); 37 | -------------------------------------------------------------------------------- /lib/ember-migrator.js: -------------------------------------------------------------------------------- 1 | var walk = require('walk-sync'); 2 | var path = require('path'); 3 | var mkdirp = require('mkdirp'); 4 | var recast = require('recast'); 5 | var string = require('underscore.string'); 6 | var fs = require('fs'); 7 | var TypedExport = require('./typed-export'); 8 | //var HelperVisitor = require('./helper-visitor'); 9 | var execSync = require('child_process').execSync; 10 | var UI = require('ember-cli/lib/ui'); 11 | var MigratorVisitor = require('./migrator-visitor'); 12 | var namedTypes = recast.types.namedTypes; 13 | var builders = recast.types.builders; 14 | var chalk = require('chalk'); 15 | var lodash = require('lodash'); 16 | 17 | // Recast helpers 18 | 19 | function EmberMigrator(options){ 20 | this.testing = options.testing || false; 21 | this.operationChalkText = "Git Move"; 22 | this.operationText = " Moving "; 23 | // Directory to process 24 | this.inputDirectory = options.inputDirectory; 25 | // Where we will place the ember-cli app 26 | this.outputDirectory = options.outputDirectory + '/app'; 27 | // If git mv is forced 28 | this.moveOrCopyCmd = options.forceOutput ? "git mv -f " : "git mv "; 29 | // If source files to stay 30 | this.keepSource = options.keepSource; 31 | if (this.keepSource) { 32 | this.moveOrCopyCmd = "cp "; 33 | this.operationChalkText = "Copy"; 34 | this.operationText = " Copying "; 35 | } 36 | // Ignore directories 37 | this.ignoreDirs = options.ignoreDirs ? options.ignoreDirs.split(',') : []; 38 | // Global app name used in input files 39 | this.rootAppName = options.rootAppName || 'App'; 40 | // Our ember-cli app name 41 | this.appName = options.appName || 'app'; 42 | // The first phase of our app will split files this map contains filenames as 43 | // keys and array of asts as values 44 | this.splitFiles = Object.create(null); 45 | // The final stage converts the files and stores in this map that first hashes by 46 | // type and then by filename 47 | this.convertedFiles = Object.create(null); 48 | // Map from class name to export path in ember-cli 49 | // E.g., App.MyClassView -> /app/views/my-class.js 50 | // Also, window.App -> app/app.js 51 | // We handle the difference between main app space and window simply with isWindowVar flag 52 | this.classesFilepathMap = Object.create(null); 53 | // Some things are stored on the window directly, e.g., the main application 54 | // E.g., window.App -> /app/app.js 55 | this.windowFilepathMap = Object.create(null); 56 | this.ui = new UI({ 57 | inputStream: process.stdin, 58 | outputStream: process.stdout 59 | }); 60 | } 61 | 62 | 63 | EmberMigrator.prototype = Object.create(null); 64 | 65 | EmberMigrator.prototype.run = function EmberMigrator_run(){ 66 | var self = this; 67 | var files = walk(this.inputDirectory); 68 | var jsFiles = []; 69 | var nonJsFiles = []; 70 | var hbsFiles = []; 71 | 72 | // Here is where we control what files we will process in the input directory 73 | files.forEach(function (file) { 74 | var fullPath = path.join(self.inputDirectory, file); 75 | var isDir = fs.lstatSync(fullPath).isDirectory(); 76 | var isJS = string.endsWith(file, '.js'); 77 | var isHbs = string.endsWith(file, '.handlebars') || string.endsWith(file, '.hbs'); 78 | var subDir = file.substring(0, file.lastIndexOf(path.sep) + 1); 79 | var isIgnoreDir = false; 80 | self.ignoreDirs.forEach(function (directory) { 81 | directory = directory + path.sep; 82 | if (subDir && subDir.substring(0, directory.length) === directory) { 83 | isIgnoreDir = true; 84 | } 85 | }); 86 | if (!isDir && !isIgnoreDir) { 87 | if (isJS) { 88 | jsFiles.push(file); 89 | } else if (isHbs) { 90 | hbsFiles.push(file); 91 | } else { 92 | nonJsFiles.push(file); 93 | } 94 | } 95 | }); 96 | 97 | //We do a two pass transpiling process: 98 | //1. Split files based on classes, only one per file allowed and create import map 99 | //2. Now that we know all the possible imports, actually process the files and rewrite them 100 | 101 | // TODO(Tony) make more functional and return outputs 102 | this.writeLine(chalk.blue('Preprocessing files')); 103 | jsFiles.forEach(this.splitFile.bind(this)); 104 | 105 | Object.keys(this.splitFiles).forEach(function(key) { 106 | var file = self.splitFiles[key]; 107 | if(fs.existsSync(file.oldFileName)) { 108 | var folder = file.outputFolderPath(); 109 | mkdirp.sync(folder); 110 | var outputPath = file.outputFilePath(); 111 | 112 | if (outputPath !== file.oldFileName && !this.testing) { 113 | this.writeLine(chalk.green(this.operationChalkText) + this.operationText + file.oldFileName + ' to ' + outputPath); 114 | execSync(this.moveOrCopyCmd + file.oldFileName + " " + outputPath); 115 | } 116 | } 117 | }, this); 118 | 119 | hbsFiles.forEach(function (filePath) { 120 | var fullPath = path.join(self.inputDirectory, filePath); 121 | var dirs = filePath.split('/'); 122 | if (dirs[0] !== 'templates') { 123 | // Make sure we go to templates dir and otherwise keep subdir placement 124 | dirs.unshift('templates'); 125 | } 126 | var outputFile = string.dasherize(dirs.join('/')); 127 | outputFile = path.join(self.outputDirectory, outputFile); 128 | var outputFolder = path.dirname(outputFile); 129 | if (fullPath === outputFile) { 130 | this.writeLine(chalk.green('No Change or Move ') + fullPath ); 131 | } 132 | else if (this.testing) { 133 | mkdirp.sync(outputFolder); 134 | fs.writeFileSync(outputFile, fs.readFileSync(fullPath).toString()); 135 | } 136 | else if (!this.testing) { 137 | this.writeLine(chalk.green(this.operationChalkText) + this.operationText + filePath + ' to ' + outputFile); 138 | mkdirp.sync(outputFolder); 139 | execSync(this.moveOrCopyCmd + fullPath + " " + outputFile); 140 | } 141 | }, this); 142 | 143 | var unknownNonJsFiles = []; 144 | 145 | nonJsFiles.forEach(function (filePath) { 146 | var fullPath = path.join(self.inputDirectory, filePath); 147 | var outputFile; 148 | 149 | if (string.endsWith(filePath, '.gitkeep')) { 150 | outputFile = path.join(self.outputDirectory, string.dasherize(filePath)); 151 | if (fullPath !== outputFile) { 152 | this.writeLine(chalk.green('No Change or Move') + ' ' + fullPath); 153 | execSync(this.moveOrCopyCmd + fullPath + " " + outputFile); 154 | return; 155 | } 156 | } else { 157 | outputFile = path.join(self.outputDirectory, 'nonjs', string.dasherize(filePath)); 158 | } 159 | var outputFolder = path.dirname(outputFile); 160 | if (fullPath === outputFile) { 161 | this.writeLine(chalk.green('No Change or Move') + ' ' + fullPath ); 162 | } else { 163 | unknownNonJsFiles.push([outputFile, outputFolder, fullPath]); 164 | } 165 | }, this); 166 | 167 | unknownNonJsFiles.forEach(function(data) { 168 | var outputFile = data[0]; 169 | var outputFolder = data[1]; 170 | var fullPath = data[2]; 171 | 172 | this.writeLine(chalk.yellow('Copying Unknown File') + ' ' + fullPath); 173 | mkdirp.sync(outputFolder); 174 | fs.writeFileSync(outputFile, fs.readFileSync(fullPath)); 175 | }, this); 176 | 177 | if (!this.testing && !this.keepSource) { 178 | execSync("git commit -m \"auto-commit from ember-cli-migrator\""); 179 | } 180 | Object.keys(this.splitFiles).forEach(function(key) { 181 | self.processFile(self.splitFiles[key]); 182 | }); 183 | 184 | console.log('flush'); 185 | this.flushConvertedFiles(this.splitFiles); 186 | 187 | if (!this.testing) { 188 | execSync("git add " + this.outputDirectory); 189 | } 190 | }; 191 | 192 | EmberMigrator.prototype.writeLine = function(message){ 193 | if (!this.testing) { 194 | return this.ui.writeLine.apply(this.ui, arguments); 195 | } 196 | } 197 | 198 | EmberMigrator.prototype.splitFile = function(filePath) { 199 | var file = fs.readFileSync(path.join(this.inputDirectory, filePath)).toString(); 200 | var oldFilePath = path.join(this.inputDirectory, filePath); 201 | try { 202 | var ast = recast.parse(file); 203 | } catch (e) { 204 | this.writeLine(chalk.red('Failed to parse ' + filePath)); 205 | if (filePath.indexOf("node_modules") > -1 || filePath.indexOf("bower_components") > -1) { 206 | this.writeLine(chalk.yellow("Note that you probably don't want to run the migrator against your npm or bower dependencies. Make sure that the directory you've specified in the '--source' flag only contains your application code.")); 207 | } 208 | this.writeLine(chalk.grey(e)); 209 | return; 210 | } 211 | var astBody = ast.program.body; 212 | // Cache of ast nodes that are not directly exported so need to be appended 213 | // at the end of the splitting process 214 | var nonExportNodes = []; 215 | // Keep track of exports we have split this file into 216 | var typedExports = []; 217 | // For some reason the first nodes leading comments are separated from the node 218 | var firstNodeComments = ast.program.comments; 219 | 220 | var that = this; 221 | var addTypedNode = function(node, filePath, className, isWindow) { 222 | var newType = TypedExport.determineType(filePath, className); 223 | var fileName = TypedExport.filePathForClassname(className, newType, filePath, that.splitFiles); 224 | 225 | if (!that.splitFiles[fileName]) { 226 | var typedExport = new TypedExport({ 227 | outputDirectory: that.outputDirectory, 228 | type: newType, 229 | fileName: fileName, 230 | // TODO(Tony) this will be a problem if a non-exporting node is found 231 | // first, because className will be null 232 | exportName: className, 233 | oldFileName: oldFilePath, 234 | appName: that.appName 235 | }); 236 | that.splitFiles[fileName] = typedExport; 237 | // Every typed export needs to be able to be looked up on this map 238 | that.classesFilepathMap[typedExport.exportName] = { 239 | moduleName: typedExport.exportPath(that.appName), 240 | isWindow: isWindow 241 | }; 242 | typedExports.push(typedExport); 243 | } 244 | that.splitFiles[fileName].astNodes.push(node); 245 | }; 246 | 247 | function extractHelpers(astBody){ 248 | var visitor = new HelperVisitor(); 249 | visitor.visit(astBody); 250 | if (visitor.results.length === 1) { 251 | console.log('YAY!') 252 | } 253 | } 254 | 255 | // Helper function to take mutliple expressions, e.g., var A = B = C, 256 | // which is represented by a tree of depth 3 and turn that into two trees 257 | // of depth 2, i.e., two assignment expressions. 258 | // 259 | // Input: {expression: { name: A, right: { name: B, right: { name: C}}}}, [] 260 | // 261 | // Output: [{expression: {name: A, right: {name: C}}}, {expression: {name: B, right: {name: C}}}] 262 | // 263 | // TODO might want to clean this up and make less sloppy with copies, but 264 | // this also happens very rarely so might not matter 265 | function flattenMultiExpression(node, flattenedNodes) { 266 | // Assume node is an assignment expression 267 | var rightNode = node.expression.right; 268 | if (namedTypes.AssignmentExpression.check(rightNode)) { 269 | // Right node is also an assignment expression so we need to flatten it, and 270 | // we want to keep the same expression oject structure for the node 271 | var newNode = lodash.cloneDeep(node); 272 | newNode.expression = rightNode; 273 | flattenMultiExpression(newNode, flattenedNodes); 274 | // Copy the rightNode assignment 275 | var cloneValue = lodash.cloneDeep(newNode.expression.right); 276 | // And now flatten ourselves, i.e., set our right side = to the value 277 | // at the end of the assignment chain 278 | node.expression.right = cloneValue; 279 | } 280 | // I've been flattened so I can go in the list 281 | flattenedNodes.unshift(node); 282 | } 283 | 284 | astBody.forEach(function(node, index) { 285 | var isNodeStored = false; 286 | var className; 287 | 288 | if (index === 0 && firstNodeComments) { 289 | node.comments = firstNodeComments; 290 | } 291 | 292 | if (namedTypes.ExpressionStatement.check(node) && 293 | namedTypes.AssignmentExpression.check(node.expression)) { 294 | 295 | // Let's get all the assignments in case of multi-assignments 296 | // E.g., var App.One = var App.Two = Ember.Object.extend 297 | // TODO This currently won't handle non-export multi-assingments, 298 | // e.g. var App.One = var Two = Ember.Object.extend, the Two will disappear 299 | var flattenedNodes = []; 300 | flattenMultiExpression(node, flattenedNodes); 301 | 302 | flattenedNodes.forEach(function(flatNode) { 303 | // We know we are an assignment 304 | var expression = flatNode.expression; 305 | if (namedTypes.MemberExpression.check(expression.left) && 306 | (expression.left.object.name === this.rootAppName || 307 | expression.left.object.name === "window")) { 308 | 309 | // See if we are assigning the class on the App or window 310 | var isWindow = expression.left.object.name === "window"; 311 | className = expression.left.property.name; 312 | addTypedNode(flatNode, filePath, className, isWindow); 313 | isNodeStored = true; 314 | } 315 | }.bind(this)); 316 | 317 | // Check to see if the expression is a commonjs export 318 | if (namedTypes.AssignmentExpression.check(node.expression) && 319 | node.expression.left.object && 320 | node.expression.left.object.name === 'module' && 321 | node.expression.left.property.name === 'exports') { 322 | if(namedTypes.Identifier.check(node.expression.right)){ 323 | addTypedNode(node, filePath, node.expression.right.name, false); 324 | isNodeStored = true; 325 | } 326 | } 327 | 328 | } 329 | 330 | if (!isNodeStored) { 331 | // Any other code that is not a global class assignment on root app will 332 | // be remembered and at the end of splitting we will append these to the 333 | // first typedExport or create a new typed export if none was created 334 | nonExportNodes.push(node); 335 | } 336 | }, this); 337 | 338 | 339 | // Append any remaining non-export nodes to either first export or new export 340 | nonExportNodes.forEach(function (node) { 341 | if (typedExports.length === 0) { 342 | var newFilePath = string.dasherize(filePath); 343 | addTypedNode(node, newFilePath); 344 | } else { 345 | typedExports[0].astNodes.push(node); 346 | } 347 | }); 348 | }; 349 | 350 | EmberMigrator.prototype.processFile = function(typedExport){ 351 | typedExport.convertedFile = this.convertFile(typedExport); 352 | }; 353 | 354 | EmberMigrator.prototype.convertFile = function(typedExport){ 355 | var visitor = new MigratorVisitor({ 356 | localAppName: this.appName, 357 | rootAppName: this.rootAppName, 358 | classesFilepathMap: this.classesFilepathMap, 359 | outputDirectory: this.outputDirectory, 360 | fileName: [typedExport.fileName] 361 | }); 362 | visitor.visit(typedExport.astNodes); 363 | var imports = Object.keys(visitor.imports) 364 | // Do not import the module we are exporting 365 | .filter(function (key) { 366 | return key !== typedExport.exportName; 367 | }) 368 | .map(function(key){ 369 | var importFilename = TypedExport.convertToOutputFilename(visitor.imports[key]); 370 | if (importFilename[0] === path.sep) { importFilename = importFilename.substring(1); } // skip leading / from file path 371 | return 'import ' + key + ' from \'' + importFilename + '\';'; 372 | }, this).join("\n"); 373 | 374 | imports = imports.replace(/\.js/g, ''); 375 | 376 | //Add two blank lines below imports if we have imports 377 | if (imports.length > 0) { 378 | imports = imports + "\n\n"; 379 | } 380 | 381 | // Convert our ast into string in order to join with imports and do final processing 382 | var astCode = typedExport.astNodes.map(function(node) { return recast.print(node).code; }).join('\n'); 383 | var code = imports + astCode + '\n'; 384 | code = code.replace(/;;\n+/g, ';\n'); // for some reason recast print will add another semicolon if there is already one 385 | 386 | // for some reason there is a rogue semicolon 387 | code = code.replace(/\n;\n/, '\n'); 388 | 389 | code = code.replace(new RegExp(this.rootAppName + "\\.", 'g'), ''); 390 | code = code.replace(/Em\./g, 'Ember.'); 391 | // For any module imported that used to be a window global 392 | Object.keys(this.classesFilepathMap).forEach(function(name) { 393 | var module = this.classesFilepathMap[name]; 394 | if (module.isWindow) { 395 | code = code.replace("window." + name, name); 396 | } 397 | }, this); 398 | 399 | code = code + "\n" + "export default " + typedExport.exportName + ";\n"; 400 | 401 | return code; 402 | }; 403 | 404 | EmberMigrator.prototype.flushConvertedFiles = function(splitFiles){ 405 | //Create directory for every path in our app including unknown folders 406 | Object.keys(splitFiles).forEach(function(file) { 407 | var typedExport = splitFiles[file]; 408 | var folder = typedExport.outputFolderPath(); 409 | mkdirp.sync(folder); 410 | fs.writeFileSync(typedExport.outputFilePath(), typedExport.convertedFile); 411 | }, this); 412 | }; 413 | 414 | 415 | function capitalize(string){ 416 | return string.charAt(0).toUpperCase() + string.slice(1); 417 | } 418 | 419 | module.exports = EmberMigrator; 420 | -------------------------------------------------------------------------------- /lib/helper-visitor.js: -------------------------------------------------------------------------------- 1 | var recast = require('recast'); 2 | var Visitor = recast.Visitor; 3 | var builders = recast.types.builders; 4 | var namedTypes = recast.types.namedTypes; 5 | 6 | // Find the helper name 7 | // Take the function out and rename it 8 | // Ember.Handlebars.helper('prettyPrint', function(){ 9 | // 10 | // }) 11 | // 12 | // becomes 13 | // 14 | // function prettyPrint(){ 15 | // 16 | // } 17 | // 18 | // then add the export default Ember.Handlebars.makeBoundHelper 19 | // 20 | // then dasherize the file 21 | 22 | // 1st pass: find functions 23 | // 2nd pass: remove Ember.Handlebars.helper statement; 24 | // 3rd pass: add `import Ember` and export default and write; 25 | 26 | var HelperVisitor = Visitor.extend({ 27 | init: function(){ 28 | this.__setupResultsArray(); 29 | }, 30 | visitCallExpression: function(node){ 31 | var callee = node.callee; 32 | var objectName = callee.object.object && callee.object.object.name; 33 | var secondName = callee.object.property && callee.object.property.name; 34 | var helperName = node.arguments[0] && node.arguments[0].value; 35 | var functionBody = node.arguments[1]; 36 | 37 | // Extract the helper 38 | if (objectName === 'Ember' && secondName === 'Handlebars') { 39 | this.results.push({ 40 | helperName: helperName, 41 | functionBody: functionBody 42 | }); 43 | this.remove(); 44 | return false; 45 | } 46 | 47 | // no helper, return the original code 48 | return node; 49 | }, 50 | 51 | __setupResultsArray: function(){ 52 | if (!this.results) { 53 | this.results = []; 54 | } 55 | } 56 | }); 57 | 58 | module.exports = HelperVisitor; 59 | -------------------------------------------------------------------------------- /lib/migrator-visitor.js: -------------------------------------------------------------------------------- 1 | var recast = require('recast'); 2 | var namedTypes = recast.types.namedTypes; 3 | var builders = recast.types.builders; 4 | var Visitor = recast.Visitor; 5 | var TypedExport = require('./typed-export'); 6 | var _ = require('lodash'); 7 | 8 | // Helper function to extract global namespace name from a member expression node 9 | function extractMemberExpressionInfo(node) { 10 | var info = {}; 11 | if (node.get('object') && node.get('object', 'object')) { 12 | // Need to check member expressions of the type Ember.ObjectController.extend 13 | info.namespace = node.get('object', 'name').value; 14 | info.property = node.get('property', 'name').value; 15 | } else if (node.get('object')) { 16 | // Need to check member expressions of the type App.FunMixin 17 | info.namespace = node.get('object').get('name').value; 18 | info.property = node.get('property').get('name').value; 19 | } 20 | return info; 21 | } 22 | 23 | function MigratorVisitor(options) { 24 | this.options = options; 25 | this.init(options); 26 | } 27 | 28 | // The primary purpose of the process visitor is to find the set of all imports we need 29 | // to construct the output file 30 | var MigratorVisitorPrototype = { 31 | init: function(options) { 32 | this.rootAppName = options.rootAppName; 33 | this.localAppName = options.localAppName; 34 | this.classesFilepathMap = options.classesFilepathMap; 35 | this.outputDirectory = options.outputDirectory; 36 | this.fileName = options.fileName; 37 | // This is what we will construct our import strings from 38 | this.imports = {}; 39 | }, 40 | 41 | visitMemberExpression: function(node){ 42 | // Check all member expressions, e.g., object.name, in order to find imports we need 43 | var info = extractMemberExpressionInfo(node); 44 | if ((info.namespace === "Ember" || info.namespace === "Em")){ 45 | // We are using Ember or Em namespace so import Ember 46 | this.imports.Ember = 'Ember'; 47 | } else if (info.namespace === "DS") { 48 | // We are using DS namespace so import ember-data 49 | this.imports.DS = 'ember-data'; 50 | } else if (info.namespace === this.rootAppName){ 51 | // We are using the global App namespace so we need to import something from our ember-cli structure 52 | var name = info.property; 53 | if (this.classesFilepathMap[name] && this.classesFilepathMap[name].moduleName) { 54 | this.imports[name] = this.classesFilepathMap[name].moduleName; 55 | } else { 56 | var file = this.fileName && this.fileName.pop(); 57 | if (file) { console.log("While converting", file); } 58 | console.log(" Do not know how to import", info.namespace + '.' + name); 59 | } 60 | } 61 | this.traverse(node); 62 | }, 63 | 64 | // The secondary purpose of this visitor is to replace the app assignment with a var assignment so that 65 | // we are ready to export the var rather than use globals 66 | visitAssignmentExpression: function(node){ 67 | this.traverse(node); 68 | var leftNode = node.value.left; 69 | var rightNode = node.value.right; 70 | if (namedTypes.MemberExpression.check(node.value.left) && 71 | (leftNode.object.name === this.rootAppName || 72 | leftNode.object.name === 'window') ){ 73 | this._exportName = leftNode.property.name; 74 | var newNode = builders.variableDeclaration("var", [builders.variableDeclarator(builders.identifier(leftNode.property.name), rightNode)]); 75 | // Recursively check this node to see if it is a member expression and we need an import 76 | //this.traverse(newNode); 77 | node.replace(newNode); 78 | return false; 79 | } 80 | 81 | var info = {}; 82 | 83 | if (leftNode && leftNode.object ) { 84 | var __namespace__ = leftNode.object.object && leftNode.object.object.name; 85 | __namespace__ = __namespace__ || leftNode.object.name; 86 | info = { 87 | namespace: __namespace__, 88 | property: leftNode.property.name 89 | }; 90 | } 91 | // check to see if the member expressions is modules.exports 92 | if(info.namespace === 'module' && 93 | info.property === 'exports') { 94 | 95 | // check if the right side is an identifier and not a direct 96 | // export of a function 97 | if(namedTypes.Identifier.check(rightNode)){ 98 | 99 | // if the export definition is "simple" ie; 100 | // module.exports = App; 101 | // remove the assignment expression 102 | node.replace(null); 103 | return false; 104 | } 105 | } 106 | 107 | }, 108 | visitVariableDeclaration: function(node){ 109 | var typedExport = TypedExport; 110 | // visit the node if the right side of the expression is a call expression 111 | // to the require function 112 | var declaration = node.value.declarations[0]; 113 | if(namedTypes.CallExpression.check(declaration.init) && 114 | declaration.init.callee.name === 'require' && 115 | declaration.init.arguments.length === 1){ 116 | var className = declaration.id.name, 117 | requirePath = declaration.init.arguments[0].raw; 118 | var newType = typedExport.determineType(requirePath, className); 119 | var fileName = typedExport.filePathForClassname(className, newType, requirePath, []) 120 | var instance = new typedExport({ 121 | type: newType, 122 | fileName: fileName, 123 | exportName: className, 124 | oldFileName: requirePath, 125 | appName: this.localAppName, 126 | outputDirectory: this.outputDirectory 127 | }) 128 | 129 | var exportPath = instance.exportPath(this.rootAppName); 130 | 131 | this.imports[className] = exportPath; 132 | node.replace(null); 133 | return false; 134 | } 135 | this.traverse(node); 136 | } 137 | }; 138 | 139 | MigratorVisitor.prototype = Object.create(MigratorVisitorPrototype); 140 | 141 | MigratorVisitor.prototype.visit = function(ast) { 142 | var obj = _.extend({}, this, MigratorVisitorPrototype); 143 | var f = recast.visit(ast, obj); 144 | return f; 145 | }; 146 | 147 | module.exports = MigratorVisitor; 148 | -------------------------------------------------------------------------------- /lib/typed-export.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var string = require('underscore.string'); 3 | 4 | // TODO(Tony) bring this into prototype 5 | function TypedExport(options) { 6 | // Each export module needs a type 7 | this.type = options.type || 'unknown'; 8 | // The ast nodes that will go into this export module 9 | this.astNodes = []; 10 | // Output directory for module files 11 | this.outputDirectory = options.outputDirectory; 12 | // Set the export name for the module 13 | this.exportName = options.exportName; 14 | this.fileName = options.fileName; 15 | this.oldFileName = options.oldFileName; 16 | this.appName = options.appName; 17 | } 18 | 19 | TypedExport.prototype = Object.create(null); 20 | 21 | TypedExport.knownTypes = [ 22 | 'model', 23 | 'serializer', 24 | 'route', 25 | 'controller', 26 | 'view', 27 | 'mixin', 28 | 'transform', 29 | 'adapter', 30 | 'component', 31 | 'service' 32 | ]; 33 | 34 | TypedExport.pluralizeType = function(type) { 35 | return type + 's'; 36 | } 37 | 38 | TypedExport.determineType = function(filePath, className) { 39 | // First check to see if any class matches 40 | var type = 'unknown'; 41 | if (!className) { 42 | return type; 43 | } 44 | 45 | // Classnames should end with the titleized type 46 | TypedExport.knownTypes.forEach(function(testType) { 47 | var r = new RegExp(string.titleize(testType) + '$'); 48 | if (r.test(className)) { 49 | type = testType; 50 | } 51 | }, this); 52 | 53 | // Check to see if filename provides type, if we did not find it from classname 54 | if (type === 'unknown') { 55 | TypedExport.knownTypes.forEach(function(testType) { 56 | var r = new RegExp(TypedExport.pluralizeType(testType)); 57 | if (r.test(filePath)) { 58 | type = testType; 59 | } 60 | }, this); 61 | } 62 | return type; 63 | } 64 | 65 | // TODO(Tony) handle path and name 66 | TypedExport.prototype.exportPath = function() { 67 | return string.dasherize('/' + this.appName + '/' + this.fileName); 68 | }; 69 | 70 | TypedExport.prototype.outputFolderPath = function() { 71 | return path.join(this.outputDirectory, path.dirname(string.dasherize(this.fileName))); 72 | }; 73 | 74 | TypedExport.prototype.outputFilePath = function() { 75 | var fileName = path.basename(this.fileName); 76 | var folderPath = this.outputFolderPath(); 77 | return path.join(folderPath, string.dasherize(fileName)); 78 | }; 79 | 80 | TypedExport.convertToOutputFilename = function(stringInput) { 81 | var filename = []; 82 | var chars = string.chars(stringInput); 83 | function isUpperCase(str) { return (str === str.toUpperCase() && !isLowerCase(str)); } 84 | function isLowerCase(str) { return (str === str.toLowerCase()); } 85 | chars.forEach(function(c, i) { 86 | if (i>0 && isLowerCase(chars[i-1]) && isUpperCase(c)) { 87 | filename.push('-'); 88 | } 89 | filename.push(c); 90 | }); 91 | return filename.join('').toLowerCase(); 92 | } 93 | 94 | TypedExport.filePathForClassname = function(className, type, filePath, exportFiles) { 95 | var newFilePath; 96 | if (type === 'unknown') { 97 | newFilePath = filePath; 98 | } else { 99 | var filename = TypedExport.convertToOutputFilename(className); 100 | 101 | var fileParts = filename.split('-'); 102 | var shouldPop = false; 103 | TypedExport.knownTypes.forEach(function(testType) { 104 | var r = new RegExp(testType); 105 | // If we are a known type and the type is on the last part of the filename remove it 106 | if (type === testType && r.test(fileParts[fileParts.length-1])) { 107 | shouldPop = true; 108 | } 109 | }); 110 | if (shouldPop) { 111 | fileParts.pop(); 112 | } 113 | filename = fileParts.join('-'); 114 | newFilePath = TypedExport.pluralizeType(type) + "/" + filename + ".js"; 115 | } 116 | 117 | // Check to see if we are colliding with previous export filenames 118 | // TODO(Tony) - className is null if we are not actually trying to export and 119 | // therefore we want it to map to an existing filename 120 | if (className && newFilePath in exportFiles) { 121 | if (type === "unknown") { 122 | var splitPath = filePath.split("/"); 123 | var filename = TypedExport.convertToOutputFilename(className); 124 | splitPath[splitPath.length - 1] = filename + ".js"; 125 | newFilePath = splitPath.join("/"); 126 | } 127 | if (newFilePath in exportFiles) { 128 | // In the rare case that we have a className, i.e., we were trying to put 129 | // something on the global App namespace, but it is going to be same to 130 | // the same split file (probably have same dasherized name, e.g., dupName, 131 | // and DupName). 132 | var nameWithoutExt = newFilePath.slice(0,newFilePath.indexOf(".js")); 133 | newFilePath = nameWithoutExt + "-x.js"; 134 | } 135 | // Don't know why we would have more than two classNames trying to map to the same export file 136 | if (newFilePath in exportFiles) { 137 | console.log('Bad things happening, multiple redundant global export for className:', className); 138 | } 139 | } 140 | return newFilePath; 141 | } 142 | 143 | module.exports = TypedExport; 144 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-migrator", 3 | "description": "migrates files over to the ember-cli structure", 4 | "version": "0.10.0", 5 | "dependencies": { 6 | "chalk": "^1.0.0", 7 | "commander": "^2.5.0", 8 | "ember-cli": "^0.2.0-beta.1", 9 | "lodash": "^3.5.0", 10 | "mkdirp": "^0.5.0", 11 | "recast": "^0.13.0", 12 | "rsvp": "^3.0.14", 13 | "underscore.string": "^2.3.3", 14 | "walk-sync": "^0.1.3" 15 | }, 16 | "bin": "./bin/ember-cli-migrator", 17 | "scripts": { 18 | "test": "mocha test" 19 | }, 20 | "devDependencies": { 21 | "chai": "^1.9.2", 22 | "chai-as-promised": "^4.1.1", 23 | "mocha": "^1.21.5", 24 | "rimraf": "^2.6.2" 25 | }, 26 | "engines": ">= 0.12.0" 27 | } 28 | -------------------------------------------------------------------------------- /test/common_js-debug-test.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var helper = require('./test_helper'); 5 | var migrates = helper.migrates; 6 | var it = helper.it; 7 | var fs = require('fs'); 8 | 9 | describe('migrating commonjs', function(){ 10 | before(function(){ 11 | this.migrator = helper.migrator({inputFixtures: 'common_js'}); 12 | this.migrator.run(); 13 | }); 14 | 15 | after(function(){ 16 | this.migrator.clean(); 17 | }); 18 | 19 | describe('Works with controllers with dependencies', function(){ 20 | it(migrates('controllers/with-mixin.js')); 21 | }); 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /test/common_js-test.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var helper = require('./test_helper'); 5 | var migrates = helper.migrates; 6 | var it = helper.it; 7 | var fs = require('fs'); 8 | 9 | describe('migrating commonjs', function(){ 10 | before(function(){ 11 | this.migrator = helper.migrator({inputFixtures: 'common_js'}); 12 | this.migrator.run(); 13 | }); 14 | 15 | after(function(){ 16 | this.migrator.clean(); 17 | }); 18 | 19 | describe('Works with application file', function(){ 20 | it(migrates('application.js')); 21 | }); 22 | 23 | describe('Works with controller files', function(){ 24 | it(migrates('controllers/comment-activity.js')); 25 | it(migrates('controllers/preserve-comments.js')); 26 | }); 27 | 28 | describe('Works with controllers with dependencies', function(){ 29 | it(migrates('controllers/with-mixin.js')); 30 | }); 31 | 32 | }); 33 | -------------------------------------------------------------------------------- /test/custom-app-name-test.js: -------------------------------------------------------------------------------- 1 | var helper = require('./test_helper'); 2 | var it = helper.it; 3 | var migrates = helper.migrates; 4 | 5 | describe('custom app name', function(){ 6 | before(function(){ 7 | this.migrator = helper.migrator({ 8 | inputFixtures: 'custom_app_name', 9 | rootAppName: 'MyApp' 10 | }); 11 | this.migrator.run(); 12 | }); 13 | 14 | after(function(){ 15 | this.migrator.clean(); 16 | }); 17 | 18 | describe('single export file', function(){ 19 | it(migrates('models/comment-activity.js')); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/ember-rails-test.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var helper = require('./test_helper'); 5 | var migrates = helper.migrates; 6 | var it = helper.it; 7 | var fs = require('fs'); 8 | 9 | describe('migrating ember-rails', function(){ 10 | before(function(){ 11 | this.migrator = helper.migrator({ 12 | inputFixtures: 'ember-rails', 13 | rootAppName: 'Pos' 14 | }); 15 | this.migrator.run(); 16 | }); 17 | 18 | after(function(){ 19 | this.migrator.clean(); 20 | }); 21 | 22 | describe('Works with application file', function(){ 23 | it(migrates('application.js')); 24 | }); 25 | 26 | }); 27 | -------------------------------------------------------------------------------- /test/fixtures/common_js/input/application.js: -------------------------------------------------------------------------------- 1 | var App = Ember.Application.extend({ 2 | someProperty: function() { 3 | console.log('hello App'); 4 | } 5 | }); 6 | 7 | module.exports = App; 8 | -------------------------------------------------------------------------------- /test/fixtures/common_js/input/controllers/comment_activity.js: -------------------------------------------------------------------------------- 1 | var CommentActivityController = Ember.ObjectController.extend({ 2 | someControllerProperty: 'props' 3 | }); 4 | 5 | module.exports = CommentActivityController; 6 | -------------------------------------------------------------------------------- /test/fixtures/common_js/input/controllers/preserve_comments.js: -------------------------------------------------------------------------------- 1 | /* Some global block 2 | * comment on multiple lines. 3 | */ 4 | 5 | // A global comment 6 | var PreserveCommentsController = Ember.ObjectController.extend({ 7 | // A comment on a property 8 | someControllerProperty: 'props', 9 | aMethod: function() { 10 | // A comment within a method 11 | console.log('hello'); 12 | } 13 | }); 14 | 15 | module.exports = PreserveCommentsController; 16 | -------------------------------------------------------------------------------- /test/fixtures/common_js/input/controllers/with_mixin.js: -------------------------------------------------------------------------------- 1 | var UsefulMixin = require("../mixins/useful_mixin"); 2 | 3 | var WithMixinController = Ember.ObjectController.extend(UsefulMixin, { 4 | someControllerProperty: 'props' 5 | }); 6 | 7 | module.exports = WithMixinController; 8 | -------------------------------------------------------------------------------- /test/fixtures/common_js/output/application.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var App = Ember.Application.extend({ 4 | someProperty: function() { 5 | console.log('hello App'); 6 | } 7 | }); 8 | 9 | export default App; 10 | -------------------------------------------------------------------------------- /test/fixtures/common_js/output/controllers/comment-activity.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var CommentActivityController = Ember.ObjectController.extend({ 4 | someControllerProperty: 'props' 5 | }); 6 | 7 | export default CommentActivityController; 8 | -------------------------------------------------------------------------------- /test/fixtures/common_js/output/controllers/preserve-comments.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | /* Some global block 4 | * comment on multiple lines. 5 | */ 6 | 7 | // A global comment 8 | var PreserveCommentsController = Ember.ObjectController.extend({ 9 | // A comment on a property 10 | someControllerProperty: 'props', 11 | aMethod: function() { 12 | // A comment within a method 13 | console.log('hello'); 14 | } 15 | }); 16 | 17 | export default PreserveCommentsController; 18 | -------------------------------------------------------------------------------- /test/fixtures/common_js/output/controllers/with-mixin.js: -------------------------------------------------------------------------------- 1 | import UsefulMixin from 'my-app/mixins/useful'; 2 | import Ember from 'ember'; 3 | 4 | 5 | var WithMixinController = Ember.ObjectController.extend(UsefulMixin, { 6 | someControllerProperty: 'props' 7 | }); 8 | 9 | export default WithMixinController; 10 | -------------------------------------------------------------------------------- /test/fixtures/custom_app_name/input/models/comment_activity.js: -------------------------------------------------------------------------------- 1 | MyApp.CommentActivity = Ember.Object.extend({ 2 | someProperty: function(){ 3 | console.log('hello'); 4 | }.property('hello') 5 | }); 6 | -------------------------------------------------------------------------------- /test/fixtures/custom_app_name/output/models/comment-activity.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var CommentActivity = Ember.Object.extend({ 4 | someProperty: function(){ 5 | console.log('hello'); 6 | }.property('hello') 7 | }); 8 | 9 | export default CommentActivity; 10 | -------------------------------------------------------------------------------- /test/fixtures/ember-rails/input/application.js: -------------------------------------------------------------------------------- 1 | //= require jquery 2 | //= require jquery_ujs 3 | //= require handlebars 4 | //= require ember 5 | //= require ember-data 6 | //= require_self 7 | //= require pos 8 | 9 | Pos = Ember.Application.create({ 10 | LOG_ACTIVE_GENERATION: true, 11 | LOG_MODULE_RESOLVER: true, 12 | LOG_TRANSITIONS: true, 13 | LOG_TRANSITIONS_INTERNAL: true, 14 | LOG_VIEW_LOOKUPS: true, 15 | }); 16 | -------------------------------------------------------------------------------- /test/fixtures/ember-rails/output/application.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | //= require jquery 4 | //= require jquery_ujs 5 | //= require handlebars 6 | //= require ember 7 | //= require ember-data 8 | //= require_self 9 | //= require pos 10 | 11 | Pos = Ember.Application.create({ 12 | LOG_ACTIVE_GENERATION: true, 13 | LOG_MODULE_RESOLVER: true, 14 | LOG_TRANSITIONS: true, 15 | LOG_TRANSITIONS_INTERNAL: true, 16 | LOG_VIEW_LOOKUPS: true, 17 | }); 18 | 19 | export default undefined; 20 | -------------------------------------------------------------------------------- /test/fixtures/helpers/input/app/helpers/reprint.js: -------------------------------------------------------------------------------- 1 | Ember.Handlebars.helper('reprint', function(thing){ 2 | return thing; 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixtures/helpers/output/app/helpers/reprint.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | function reprint(thing) { 4 | return thing; 5 | } 6 | 7 | export default Ember.Handlebars.makeBoundHelper(reprint); 8 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/application.js: -------------------------------------------------------------------------------- 1 | window.App = Ember.Application.extend({ 2 | someProperty: function() { 3 | console.log('hello App'); 4 | } 5 | }); 6 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/components/kiwi-phone.js: -------------------------------------------------------------------------------- 1 | App.KiwiPhoneComponent = Ember.Component.extend({ 2 | classNames: 'form-group', 3 | 4 | inputElementId: function(){ 5 | return 'input-' + this.get('elementId'); 6 | }.property('elementId'), 7 | 8 | translatedLabel: function(){ 9 | return translate(this.get('label')); 10 | }.property('label'), 11 | 12 | actions: { 13 | savePhoneNumber: function(phoneNumber){ 14 | this.sendAction('action', phoneNumber); 15 | } 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/controllers/comment_activity.js: -------------------------------------------------------------------------------- 1 | App.CommentActivityController = Ember.ObjectController.extend({ 2 | someControllerProperty: 'props' 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/controllers/preserve_comments.js: -------------------------------------------------------------------------------- 1 | /* Some global block 2 | * comment on multiple lines. 3 | */ 4 | 5 | // A global comment 6 | App.PreserveCommentsController = Ember.ObjectController.extend({ 7 | // A comment on a property 8 | someControllerProperty: 'props', 9 | aMethod: function() { 10 | // A comment within a method 11 | console.log('hello'); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/controllers/with_mixin.js: -------------------------------------------------------------------------------- 1 | App.WithMixinController = Ember.ObjectController.extend(App.UsefulMixin, { 2 | someControllerProperty: 'props' 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/mixins/coffee_mixin.js.coffee: -------------------------------------------------------------------------------- 1 | App.CoffeeMixin = Ember.Mixin.create 2 | latte: 'like' 3 | espresso: 'like more' 4 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/mixins/useful.js: -------------------------------------------------------------------------------- 1 | App.UsefulMixin = Ember.Mixin.create({ 2 | useThisProperty: 'props' 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/models/comment_activity.js: -------------------------------------------------------------------------------- 1 | App.CommentActivity = Ember.Object.extend({ 2 | someProperty: function(){ 3 | console.log('hello'); 4 | }.property('hello') 5 | }); 6 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/models/comment_activity_should_ignore.js.erb: -------------------------------------------------------------------------------- 1 | App.CommentActivity = Ember.Object.extend({ 2 | erbProperty: "<%= someDumbServerPropertyTooLazyMakeAPI %>", 3 | someProperty: function(){ 4 | console.log('hello'); 5 | }.property('hello') 6 | }); 7 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/models/comment_activity_with_ds.js: -------------------------------------------------------------------------------- 1 | App.CommentActivityWithDS = DS.Model.extend({ 2 | title: DS.attr('string'), 3 | someProperty: function(){ 4 | console.log('hello'); 5 | }.property('hello') 6 | }); 7 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/models/comment_activity_with_em.js: -------------------------------------------------------------------------------- 1 | App.CommentActivityWithEm = Em.Object.extend({ 2 | someProperty: function(){ 3 | console.log('hello'); 4 | }.property('hello') 5 | }); 6 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/models/comment_activity_with_path_for_type.js: -------------------------------------------------------------------------------- 1 | App.CommentActivityWithPathForType = Ember.Object.extend({ 2 | someProperty: function(){ 3 | console.log('hello'); 4 | }.property('hello') 5 | }); 6 | 7 | App.CommentActivityWithPathForType.pathForType = "Hello"; 8 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/models/extended_comment_activity.js: -------------------------------------------------------------------------------- 1 | App.ExtendedCommentActivity = App.CommentActivity.extend({ 2 | someProperty: function(){ 3 | console.log('hello'); 4 | }.property('hello') 5 | }); 6 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/models/no_import.js: -------------------------------------------------------------------------------- 1 | App.NoImport = { hello: 'hello' }; 2 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/models/user_model_with_serializer.js: -------------------------------------------------------------------------------- 1 | App.User = Ember.Object.extend({ 2 | someProperty: function(){ 3 | console.log('hello'); 4 | }.property('hello') 5 | }); 6 | 7 | App.UserSerializer = DS.Serializer.extend({ 8 | someProperty: function(){ 9 | console.log('hello'); 10 | }.property('hello') 11 | }); 12 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/router.js: -------------------------------------------------------------------------------- 1 | App.Router = Ember.Router.extend({ 2 | someProperty: function() { 3 | console.log('hello'); 4 | } 5 | }); 6 | 7 | App.Router.reopen({ 8 | location: 'auto' 9 | }); 10 | 11 | App.Router.map(function() { 12 | this.resource('myresource', {path:'/myresource/:resource_id'}, function(){ 13 | this.route('details'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/routes/index_route.js: -------------------------------------------------------------------------------- 1 | App.IndexRoute = Ember.Route.extend({ 2 | }); 3 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/routes/one_and_two_route.js: -------------------------------------------------------------------------------- 1 | App.OneRoute = App.TwoRoute = Ember.Route.extend({}); 2 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/serializers/comment_activity.js: -------------------------------------------------------------------------------- 1 | App.CommentActivitySerializer = DS.Serializer.extend({ 2 | someProperty: function(){ 3 | console.log('hello'); 4 | }.property('hello') 5 | }); 6 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/services/seattle-alert.js: -------------------------------------------------------------------------------- 1 | App.SeattleAlertService = Ember.Object.extend({ 2 | alerts: Ember.A(), 3 | 4 | success: function(context, opts) { 5 | opts.style = 'success'; 6 | this.create(context, opts); 7 | }, 8 | 9 | create: function(context, opts) { 10 | var defaults = { 11 | hideClose: false, 12 | style: 'default', 13 | }; 14 | 15 | Ember.merge(defaults, opts); 16 | 17 | var alert = Ember.ObjectProxy.extend({ 18 | content: context, 19 | options: defaults, 20 | }).create(); 21 | 22 | this.get('alerts').pushObject(alert); 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/store.js: -------------------------------------------------------------------------------- 1 | App.ApplicationAdapter = DS.ActiveModelAdapter.extend({ 2 | namespace: 'v2/api' 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/templates/atemplate.handlebars: -------------------------------------------------------------------------------- 1 | {{outlet}} 2 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/templates/components/anothertemplate.hbs: -------------------------------------------------------------------------------- 1 | {{outlet}} 2 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/transforms/objecttransform.js: -------------------------------------------------------------------------------- 1 | App.ObjectTransform = DS.Transform.extend({ 2 | serialize: function(value) { 3 | return value; 4 | }, 5 | deserialize: function(value) { 6 | if(Ember.isArray(value)){ 7 | return value.map(function(v){ 8 | return ("object" == typeof v) ? Ember.Object.create(v) : v; 9 | }); 10 | }else if("object" == typeof value){ 11 | return Ember.Object.create(value); 12 | } 13 | return value; 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/unknown_type/known_type.js: -------------------------------------------------------------------------------- 1 | App.KnownTypeMixin = Ember.Mixin.create({ 2 | iKnowStuff: 'stuff' 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/unknown_type/misc.js: -------------------------------------------------------------------------------- 1 | App.Misc = Ember.Object.extend({ 2 | someProperty: function(){ 3 | console.log('hello'); 4 | }.property('hello') 5 | }); 6 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/unknown_type/misc_long_name.js: -------------------------------------------------------------------------------- 1 | App.MiscLongName = Ember.Object.extend({ 2 | someProperty: function(){ 3 | console.log('hello'); 4 | }.property('hello') 5 | }); 6 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/views/comment_activity.js: -------------------------------------------------------------------------------- 1 | App.CommentActivityView = Ember.View.extend({ 2 | someViewProperty: 'props' 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/views/duplicate_name.js: -------------------------------------------------------------------------------- 1 | App.DuplicateName = Ember.Object.extend({ 2 | hello: function() { 3 | console.log('hello'); 4 | } 5 | }); 6 | 7 | App.duplicateName = App.DuplicateName.create(); 8 | 9 | App.SomeUnknownType = Ember.Object.extend({ 10 | helloAgain: function() { 11 | console.log('hello'); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/views/reopen_view.js: -------------------------------------------------------------------------------- 1 | App.ReopenView = Ember.View.extend({ 2 | hello: function () { 3 | console.log('hello'); 4 | } 5 | }); 6 | 7 | App.ReopenView.reopen({ 8 | helloAgain: function () { 9 | console.log('hi'); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/views/should_be_in_templates.handlebars: -------------------------------------------------------------------------------- 1 |

This should be moved to templates folder.

2 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/input/views/use_duplicates.js: -------------------------------------------------------------------------------- 1 | App.UseDuplicates = App.DuplicateName.extend({ 2 | init: function() { 3 | App.duplicateName.hello(); 4 | }, 5 | hello: function() { 6 | console.log('hi'); 7 | }, 8 | helloAgain: App.SomeUnknownType.create() 9 | }); 10 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/adapters/application.js: -------------------------------------------------------------------------------- 1 | import DS from 'ember-data'; 2 | 3 | var ApplicationAdapter = DS.ActiveModelAdapter.extend({ 4 | namespace: 'v2/api' 5 | }); 6 | 7 | export default ApplicationAdapter; 8 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/application.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var App = Ember.Application.extend({ 4 | someProperty: function() { 5 | console.log('hello App'); 6 | } 7 | }); 8 | 9 | export default App; 10 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/components/kiwi-phone.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var KiwiPhoneComponent = Ember.Component.extend({ 4 | classNames: 'form-group', 5 | 6 | inputElementId: function(){ 7 | return 'input-' + this.get('elementId'); 8 | }.property('elementId'), 9 | 10 | translatedLabel: function(){ 11 | return translate(this.get('label')); 12 | }.property('label'), 13 | 14 | actions: { 15 | savePhoneNumber: function(phoneNumber){ 16 | this.sendAction('action', phoneNumber); 17 | } 18 | } 19 | }); 20 | 21 | export default KiwiPhoneComponent; 22 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/controllers/comment-activity.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var CommentActivityController = Ember.ObjectController.extend({ 4 | someControllerProperty: 'props' 5 | }); 6 | 7 | export default CommentActivityController; 8 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/controllers/preserve-comments.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | /* Some global block 4 | * comment on multiple lines. 5 | */ 6 | 7 | // A global comment 8 | var PreserveCommentsController = Ember.ObjectController.extend({ 9 | // A comment on a property 10 | someControllerProperty: 'props', 11 | aMethod: function() { 12 | // A comment within a method 13 | console.log('hello'); 14 | } 15 | }); 16 | 17 | export default PreserveCommentsController; 18 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/controllers/with-mixin.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import UsefulMixin from 'my-app/mixins/useful'; 3 | 4 | var WithMixinController = Ember.ObjectController.extend(UsefulMixin, { 5 | someControllerProperty: 'props' 6 | }); 7 | 8 | export default WithMixinController; 9 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/mixins/known-type.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var KnownTypeMixin = Ember.Mixin.create({ 4 | iKnowStuff: 'stuff' 5 | }); 6 | 7 | export default KnownTypeMixin; 8 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/mixins/useful.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var UsefulMixin = Ember.Mixin.create({ 4 | useThisProperty: 'props' 5 | }); 6 | 7 | export default UsefulMixin; 8 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/models/comment-activity-with-ds.js: -------------------------------------------------------------------------------- 1 | import DS from 'ember-data'; 2 | 3 | var CommentActivityWithDS = DS.Model.extend({ 4 | title: DS.attr('string'), 5 | someProperty: function(){ 6 | console.log('hello'); 7 | }.property('hello') 8 | }); 9 | 10 | export default CommentActivityWithDS; 11 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/models/comment-activity-with-em.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var CommentActivityWithEm = Ember.Object.extend({ 4 | someProperty: function(){ 5 | console.log('hello'); 6 | }.property('hello') 7 | }); 8 | 9 | export default CommentActivityWithEm; 10 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/models/comment-activity-with-path-for-type.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var CommentActivityWithPathForType = Ember.Object.extend({ 4 | someProperty: function(){ 5 | console.log('hello'); 6 | }.property('hello') 7 | }); 8 | CommentActivityWithPathForType.pathForType = "Hello"; 9 | 10 | export default CommentActivityWithPathForType; 11 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/models/comment-activity.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var CommentActivity = Ember.Object.extend({ 4 | someProperty: function(){ 5 | console.log('hello'); 6 | }.property('hello') 7 | }); 8 | 9 | export default CommentActivity; 10 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/models/extended-comment-activity.js: -------------------------------------------------------------------------------- 1 | import CommentActivity from 'my-app/models/comment-activity'; 2 | 3 | var ExtendedCommentActivity = CommentActivity.extend({ 4 | someProperty: function(){ 5 | console.log('hello'); 6 | }.property('hello') 7 | }); 8 | 9 | export default ExtendedCommentActivity; 10 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/models/no-import.js: -------------------------------------------------------------------------------- 1 | var NoImport = { hello: 'hello' }; 2 | 3 | export default NoImport; 4 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/models/user.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var User = Ember.Object.extend({ 4 | someProperty: function(){ 5 | console.log('hello'); 6 | }.property('hello') 7 | }); 8 | 9 | export default User; 10 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/nonjs/mixins/coffee-mixin.js.coffee: -------------------------------------------------------------------------------- 1 | App.CoffeeMixin = Ember.Mixin.create 2 | latte: 'like' 3 | espresso: 'like more' 4 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/nonjs/models/comment-activity-should-ignore.js.erb: -------------------------------------------------------------------------------- 1 | App.CommentActivity = Ember.Object.extend({ 2 | erbProperty: "<%= someDumbServerPropertyTooLazyMakeAPI %>", 3 | someProperty: function(){ 4 | console.log('hello'); 5 | }.property('hello') 6 | }); 7 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/router.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var Router = Ember.Router.extend({ 4 | someProperty: function() { 5 | console.log('hello'); 6 | } 7 | }); 8 | Router.reopen({ 9 | location: 'auto' 10 | }); 11 | Router.map(function() { 12 | this.resource('myresource', {path:'/myresource/:resource_id'}, function(){ 13 | this.route('details'); 14 | }); 15 | }); 16 | 17 | export default Router; 18 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/routes/index.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var IndexRoute = Ember.Route.extend({ 4 | }); 5 | 6 | export default IndexRoute; 7 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/routes/one.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var OneRoute = Ember.Route.extend({}); 4 | 5 | export default OneRoute; 6 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/routes/two.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var TwoRoute = Ember.Route.extend({}); 4 | 5 | export default TwoRoute; 6 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/serializers/comment-activity.js: -------------------------------------------------------------------------------- 1 | import DS from 'ember-data'; 2 | 3 | var CommentActivitySerializer = DS.Serializer.extend({ 4 | someProperty: function(){ 5 | console.log('hello'); 6 | }.property('hello') 7 | }); 8 | 9 | export default CommentActivitySerializer; 10 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/serializers/user.js: -------------------------------------------------------------------------------- 1 | import DS from 'ember-data'; 2 | 3 | var UserSerializer = DS.Serializer.extend({ 4 | someProperty: function(){ 5 | console.log('hello'); 6 | }.property('hello') 7 | }); 8 | 9 | export default UserSerializer; 10 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/services/seattle-alert.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var SeattleAlertService = Ember.Object.extend({ 4 | alerts: Ember.A(), 5 | 6 | success: function(context, opts) { 7 | opts.style = 'success'; 8 | this.create(context, opts); 9 | }, 10 | 11 | create: function(context, opts) { 12 | var defaults = { 13 | hideClose: false, 14 | style: 'default', 15 | }; 16 | 17 | Ember.merge(defaults, opts); 18 | 19 | var alert = Ember.ObjectProxy.extend({ 20 | content: context, 21 | options: defaults, 22 | }).create(); 23 | 24 | this.get('alerts').pushObject(alert); 25 | } 26 | }); 27 | 28 | export default SeattleAlertService; 29 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/templates/atemplate.handlebars: -------------------------------------------------------------------------------- 1 | {{outlet}} 2 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/templates/components/anothertemplate.hbs: -------------------------------------------------------------------------------- 1 | {{outlet}} 2 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/templates/views/should-be-in-templates.handlebars: -------------------------------------------------------------------------------- 1 |

This should be moved to templates folder.

2 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/transforms/object.js: -------------------------------------------------------------------------------- 1 | import DS from 'ember-data'; 2 | import Ember from 'ember'; 3 | 4 | var ObjectTransform = DS.Transform.extend({ 5 | serialize: function(value) { 6 | return value; 7 | }, 8 | deserialize: function(value) { 9 | if(Ember.isArray(value)){ 10 | return value.map(function(v){ 11 | return ("object" == typeof v) ? Ember.Object.create(v) : v; 12 | }); 13 | }else if("object" == typeof value){ 14 | return Ember.Object.create(value); 15 | } 16 | return value; 17 | } 18 | }); 19 | 20 | export default ObjectTransform; 21 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/unknown-type/misc-long-name.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var MiscLongName = Ember.Object.extend({ 4 | someProperty: function(){ 5 | console.log('hello'); 6 | }.property('hello') 7 | }); 8 | 9 | export default MiscLongName; 10 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/unknown-type/misc.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var Misc = Ember.Object.extend({ 4 | someProperty: function(){ 5 | console.log('hello'); 6 | }.property('hello') 7 | }); 8 | 9 | export default Misc; 10 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/views/comment-activity.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var CommentActivityView = Ember.View.extend({ 4 | someViewProperty: 'props' 5 | }); 6 | 7 | export default CommentActivityView; 8 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/views/duplicate-name-x.js: -------------------------------------------------------------------------------- 1 | import DuplicateName from 'my-app/views/duplicate-name'; 2 | 3 | var duplicateName = DuplicateName.create(); 4 | 5 | export default duplicateName; 6 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/views/duplicate-name.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var DuplicateName = Ember.Object.extend({ 4 | hello: function() { 5 | console.log('hello'); 6 | } 7 | }); 8 | 9 | export default DuplicateName; 10 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/views/reopen.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var ReopenView = Ember.View.extend({ 4 | hello: function () { 5 | console.log('hello'); 6 | } 7 | }); 8 | ReopenView.reopen({ 9 | helloAgain: function () { 10 | console.log('hi'); 11 | } 12 | }); 13 | 14 | export default ReopenView; 15 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/views/some-unknown-type.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var SomeUnknownType = Ember.Object.extend({ 4 | helloAgain: function() { 5 | console.log('hello'); 6 | } 7 | }); 8 | 9 | export default SomeUnknownType; 10 | -------------------------------------------------------------------------------- /test/fixtures/vanilla/output/views/use-duplicates.js: -------------------------------------------------------------------------------- 1 | import DuplicateName from 'my-app/views/duplicate-name'; 2 | import duplicateName from 'my-app/views/duplicate-name-x'; 3 | import SomeUnknownType from 'my-app/views/some-unknown-type'; 4 | 5 | var UseDuplicates = DuplicateName.extend({ 6 | init: function() { 7 | duplicateName.hello(); 8 | }, 9 | hello: function() { 10 | console.log('hi'); 11 | }, 12 | helloAgain: SomeUnknownType.create() 13 | }); 14 | 15 | export default UseDuplicates; 16 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | 2 | --ui=bdd 3 | -------------------------------------------------------------------------------- /test/models-test.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var helper = require('./test_helper'); 5 | var migrates = helper.migrates; 6 | var it = helper.it; 7 | var fs = require('fs'); 8 | 9 | describe('migrating models', function(){ 10 | before(function(){ 11 | this.migrator = helper.migrator({inputFixtures: 'vanilla'}); 12 | this.migrator.run(); 13 | }); 14 | 15 | after(function(){ 16 | this.migrator.clean(); 17 | }); 18 | 19 | describe('single export file (only has one global)', function(){ 20 | it(migrates('models/comment-activity.js')); 21 | }); 22 | 23 | describe('Extending model classes', function(){ 24 | it(migrates('models/extended-comment-activity.js')); 25 | }); 26 | 27 | describe('Works with files with no imports', function(){ 28 | it(migrates('models/no-import.js')); 29 | }); 30 | 31 | describe('Works with Em', function(){ 32 | it(migrates('models/comment-activity-with-em.js')); 33 | }); 34 | 35 | describe('Works with Ember Data', function(){ 36 | it(migrates('models/comment-activity-with-ds.js')); 37 | }); 38 | 39 | describe('Works with serializers', function(){ 40 | it(migrates('serializers/comment-activity.js')); 41 | }); 42 | 43 | describe('Works with models and serializers in the same file', function(){ 44 | it(migrates('models/user.js')); 45 | it(migrates('serializers/user.js')); 46 | }); 47 | 48 | describe('Works with files which reopen existing classes multiple times', function(){ 49 | it(migrates('models/comment-activity.js')); 50 | }); 51 | 52 | describe('Works with simple views', function(){ 53 | it(migrates('views/comment-activity.js')); 54 | }); 55 | 56 | describe('Works with simple controllers', function(){ 57 | it(migrates('controllers/comment-activity.js')); 58 | }); 59 | 60 | describe('Works with simple mixins', function(){ 61 | it(migrates('mixins/useful.js')); 62 | }); 63 | 64 | describe('Works with known types inside unknown type folders', function(){ 65 | it(migrates('mixins/known-type.js')); 66 | }); 67 | 68 | describe('Works with unknown types inside unknown type folders', function(){ 69 | it(migrates('unknown_type/misc.js')); 70 | }); 71 | 72 | describe('Works with unkown types on root app directory', function(){ 73 | it(migrates('router.js')); 74 | }); 75 | 76 | describe('Works with application file', function(){ 77 | it(migrates('application.js')); 78 | }); 79 | 80 | describe('Works with duplicate file names', function(){ 81 | it(migrates('views/duplicate-name.js')); 82 | it(migrates('views/duplicate-name-x.js')); 83 | it(migrates('views/some-unknown-type.js')); 84 | it(migrates('views/use-duplicates.js')); 85 | }); 86 | 87 | describe('Works with transforms', function(){ 88 | it(migrates('transforms/object.js')); 89 | }); 90 | 91 | describe('Works with components', function(){ 92 | it(migrates('components/kiwi-phone.js')); 93 | }); 94 | 95 | describe('Works with servies', function(){ 96 | it(migrates('services/seattle-alert.js')); 97 | }); 98 | 99 | describe('Works with adapters', function(){ 100 | it(migrates('adapters/application.js')); 101 | 102 | it('does not copy the store', function(){ 103 | var file = path.join(this.migrator.outputDirectory, 'my-app', 'store.js'); 104 | assert(!fs.existsSync(file), 'store.js should not exist'); 105 | }); 106 | }); 107 | 108 | describe('Works with dasherized unknown type filenames', function(){ 109 | it(migrates('unknown_type/misc-long-name.js')); 110 | }); 111 | 112 | describe('Copies nonjs files to nonjs directory', function(){ 113 | it(migrates('nonjs/mixins/coffee_mixin.js.coffee')); 114 | it(migrates('nonjs/models/comment_activity_should_ignore.js.erb')); 115 | }); 116 | 117 | describe('Copies templates to templates dir', function(){ 118 | it(migrates('templates/atemplate.handlebars')); 119 | it(migrates('templates/components/anothertemplate.hbs')); 120 | it(migrates('templates/views/should_be_in_templates.handlebars')); 121 | }); 122 | 123 | describe('Handles reopen in same file', function(){ 124 | it(migrates('views/reopen.js')); 125 | }); 126 | 127 | describe('Copies routes correctly', function(){ 128 | it(migrates('routes/index.js')); 129 | }); 130 | 131 | describe('Preserve comments', function(){ 132 | it(migrates('controllers/preserve-comments.js')); 133 | }); 134 | 135 | describe('Works with multiple assignments per line', function(){ 136 | it(migrates('routes/one.js')); 137 | it(migrates('routes/two.js')); 138 | }); 139 | 140 | describe('Can mix-in mixins', function(){ 141 | it(migrates('controllers/with-mixin.js')); 142 | }); 143 | 144 | }); 145 | -------------------------------------------------------------------------------- /test/test_helper.js: -------------------------------------------------------------------------------- 1 | var rimraf = require('rimraf'); 2 | var path = require('path'); 3 | var EmberMigrator = require('../lib/ember-migrator'); 4 | var fs = require('fs'); 5 | var inflector = require('underscore.string'); 6 | var assert = require('chai').assert; 7 | var _ = require('lodash'); 8 | 9 | var tmpDir = path.join(__dirname, "../tmp"); 10 | 11 | function migrator(options) { 12 | var defaults = { 13 | inputDirectory: path.join(__dirname, "fixtures/" + options.inputFixtures + '/input'), 14 | outputDirectory: tmpDir, 15 | appName: 'my-app', 16 | testing: true 17 | }; 18 | var opts = _.extend({}, defaults, options); 19 | 20 | var migrator = new EmberMigrator(opts); 21 | 22 | migrator.expectedOutputFixtureDirectory = path.join(__dirname, "fixtures/", opts.inputFixtures, '/output'); 23 | migrator.clean = function(){ 24 | rimraf.sync(tmpDir); 25 | } 26 | return migrator; 27 | } 28 | 29 | function migratorResult(migrator, fixtureName){ 30 | var file = path.join(migrator.outputDirectory, fixtureName); 31 | return fs.readFileSync(file).toString().split('\n');; 32 | } 33 | 34 | function fixture(migrator, fixtureName){ 35 | var outDir = path.join(migrator.expectedOutputFixtureDirectory, fixtureName); 36 | return fs.readFileSync(outDir).toString().split('\n'); 37 | } 38 | 39 | function migratesCorrectly(inputFileName, outputFileName) { 40 | outputFileName = outputFileName || inflector.dasherize(inputFileName); 41 | var spec = function(){ 42 | var migrator = this.migrator; 43 | var underscored = inflector.underscored(inputFileName); 44 | var expected = fixture(migrator, outputFileName); 45 | var actual = migratorResult(migrator, inflector.dasherize(inputFileName)); 46 | assert.deepEqual(actual, expected); 47 | }; 48 | return ['migrates ' + inputFileName + ' correctly', spec]; 49 | } 50 | 51 | module.exports = migrator; 52 | module.exports.migrator = migrator; 53 | module.exports.migrates = migratesCorrectly; 54 | 55 | module.exports.it = function(result){ 56 | if (!Array.isArray(result)) { 57 | return it(result, arguments[1]); 58 | } 59 | return it(result[0], result[1]); 60 | } 61 | 62 | module.exports.it.only = function(result){ 63 | if (!Array.isArray(result)) { 64 | return it.only(result, arguments[1]); 65 | } 66 | return it.only(result[0], result[1]); 67 | }; 68 | --------------------------------------------------------------------------------