diff --git a/README.md b/README.md index 580c842..be2f90e 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,6 @@ The project uses [recast](https://github.com/benjamn/recast) (which uses Esprima - App.Router = Ember.Router.extend(); placed at beginning of router file # TODOS -- [ ] helpers -- [ ] articles-controller-mixin +- [ ] export undefined - [ ] use new visitor syntax - [ ] App.Something = App.SomethingElse = Ember.Object.extend(); diff --git a/lib/ember-migrator.js b/lib/ember-migrator.js index 04df109..8fec5ef 100644 --- a/lib/ember-migrator.js +++ b/lib/ember-migrator.js @@ -28,11 +28,7 @@ function EmberMigrator(options){ // Map from class name to export path in ember-cli // E.g., App.MyClassView -> /app/views/my-class.js // Also, window.App -> app/app.js - // We handle the difference between main app space and window simply with isWindowVar flag this.classesFilepathMap = Object.create(null); - // Some things are stored on the window directly, e.g., the main application - // E.g., window.App -> /app/app.js - this.windowFilepathMap = Object.create(null); } var Visitor = recast.Visitor; @@ -43,6 +39,7 @@ var MigratorVisitor = Visitor.extend({ init: function(options) { this.rootAppName = options.rootAppName; this.classesFilepathMap = options.classesFilepathMap; + this.filePath = options.filePath; // This is what we will construct our import strings from this.imports = {}; }, @@ -58,11 +55,15 @@ var MigratorVisitor = Visitor.extend({ this.imports.DS = 'ember-data'; } else if (isExpression && node.object.object.name === this.rootAppName){ // We are using the global App namespace so we need to import something from our ember-cli structure - var name = node.object.property.name; - if (this.classesFilepathMap[name].moduleName) { - this.imports[name] = this.classesFilepathMap[name].moduleName; + if (node.object.property.type === 'Identifier') { + var name = node.object.property.name; + if (this.classesFilepathMap[name].moduleName) { + this.imports[name] = this.classesFilepathMap[name].moduleName; + } else { + console.log("Do not know how to import", name); + } } else { - console.log("Do not know how to import", name); + console.log("Warning: Don't know how to import line in " + this.filePath); } } return node; @@ -75,7 +76,7 @@ var MigratorVisitor = Visitor.extend({ var rightNode = node.right; if (namedTypes.MemberExpression.check(node.left) && (leftNode.object.name === this.rootAppName || - leftNode.object.name === 'window') ){ + isAppDefinition(leftNode, this.rootAppName)) ) { this._exportName = leftNode.property.name; var newNode = builders.variableDeclaration("var", [builders.variableDeclarator(builders.identifier(leftNode.property.name), rightNode)]); // Recursively check this node to see if it is a member expression and we need an import @@ -132,7 +133,6 @@ EmberMigrator.prototype.run = function EmberMigrator_run(){ var fullPath = path.join(self.inputDirectory, filePath); var outputFile = path.join(self.outputDirectory, self.appName, 'nonjs', filePath); var outputFolder = path.dirname(outputFile); - console.log('copying nonjs file', outputFile); mkdirp.sync(outputFolder); fs.writeFileSync(outputFile, fs.readFileSync(fullPath)); }); @@ -147,7 +147,6 @@ EmberMigrator.prototype.run = function EmberMigrator_run(){ var outputFile = dirs.join('/'); outputFile = path.join(self.outputDirectory, self.appName, outputFile); var outputFolder = path.dirname(outputFile); - console.log('copying nonjs file', outputFile); mkdirp.sync(outputFolder); fs.writeFileSync(outputFile, fs.readFileSync(fullPath)); }); @@ -166,7 +165,7 @@ EmberMigrator.prototype.splitFile = function(filePath) { var typedExports = []; var that = this; - var addTypedNode = function(node, filePath, className, isWindow) { + var addTypedNode = function(node, filePath, className) { var newType = TypedExport.determineType(filePath, className); var fileName = TypedExport.filePathForClassname(className, newType, filePath, that.splitFiles); @@ -180,11 +179,13 @@ EmberMigrator.prototype.splitFile = function(filePath) { exportName: className }); that.splitFiles[fileName] = typedExport; - // Every typed export needs to be able to be looked up on this map - that.classesFilepathMap[typedExport.exportName] = { - moduleName: typedExport.exportPath(that.appName), - isWindow: isWindow - }; + if (typedExport.exportName) { + // Every module we export needs to be looked up on this map for imports + that.classesFilepathMap[typedExport.exportName] = { + moduleName: typedExport.exportPath(that.appName) + }; + } + // Keep track of all exports so far typedExports.push(typedExport); } that.splitFiles[fileName].astNodes.push(node); @@ -197,15 +198,14 @@ EmberMigrator.prototype.splitFile = function(filePath) { if (namedTypes.ExpressionStatement.check(node)) { // We know we are an assignment var expression = node.expression; + // Special case app definition to export, all other window exports + // should not be handled this way if (namedTypes.AssignmentExpression.check(expression) && namedTypes.MemberExpression.check(expression.left) && (expression.left.object.name === this.rootAppName || - expression.left.object.name === "window")) { - - // See if we are assigning the class on the App or window - var isWindow = expression.left.object.name === "window"; + isAppDefinition(expression.left, this.rootAppName))) { className = expression.left.property.name; - addTypedNode(node, filePath, className, isWindow); + addTypedNode(node, filePath, className); isNodeStored = true; assignmentCount++; } @@ -237,7 +237,8 @@ EmberMigrator.prototype.processFile = function(typedExport){ EmberMigrator.prototype.convertFile = function(typedExport){ var visitor = new MigratorVisitor({ rootAppName: this.rootAppName, - classesFilepathMap: this.classesFilepathMap + classesFilepathMap: this.classesFilepathMap, + filePath: typedExport.fileName }); visitor.visit(typedExport.astNodes); var imports = Object.keys(visitor.imports) @@ -263,15 +264,6 @@ EmberMigrator.prototype.convertFile = function(typedExport){ code = code.replace(/;;\n+/g, ';\n'); // for some reason recast print will add another semicolon if there is already one code = code.replace(new RegExp(this.rootAppName + "\\.", 'g'), ''); code = code.replace(/Em\./g, 'Ember.'); - // For any module imported that used to be a window global - Object.keys(this.classesFilepathMap).forEach(function(name) { - var module = this.classesFilepathMap[name]; - if (module.isWindow) { - code = code.replace("window." + name, name); - } - }, this); - - code = code + "\n" + "export default " + typedExport.exportName + ";\n"; return code; @@ -288,6 +280,10 @@ EmberMigrator.prototype.flushConvertedFiles = function(splitFiles){ }; +function isAppDefinition(leftNode, appName) { + return leftNode.object.name === "window" && leftNode.property.name === appName; +} + function capitalize(string){ return string.charAt(0).toUpperCase() + string.slice(1); } diff --git a/lib/typed-export.js b/lib/typed-export.js index 959ac14..794cd39 100644 --- a/lib/typed-export.js +++ b/lib/typed-export.js @@ -20,7 +20,7 @@ TypedExport.knownTypes = ['model', 'serializer', 'controller', 'view', 'mixin', TypedExport.pluralizeType = function(type) { return type + 's'; -} +}; TypedExport.determineType = function(filePath, className) { // First check to see if any class matches @@ -46,7 +46,7 @@ TypedExport.determineType = function(filePath, className) { }, this); } return type; -} +}; // TODO(Tony) handle path and name TypedExport.prototype.exportPath = function(appName) { @@ -79,7 +79,11 @@ TypedExport.convertToOutputFilename = function(stringInput) { TypedExport.filePathForClassname = function(className, type, filePath, exportFiles) { var newFilePath; - if (type === 'unknown') { + if (!className) { + // className = null means we are not exporting anything for this filepath + // so we should send it to special globals folder + newFilePath = path.join('globals', filePath); + } else if (type === 'unknown') { newFilePath = filePath; } else { var filename = TypedExport.convertToOutputFilename(className); diff --git a/test/fixtures/vanilla/input/analytics/global_analytics.js b/test/fixtures/vanilla/input/analytics/global_analytics.js new file mode 100644 index 0000000..71ddd11 --- /dev/null +++ b/test/fixtures/vanilla/input/analytics/global_analytics.js @@ -0,0 +1,3 @@ +(function() { + window.global_analytics = 'inject thorough analytics solution'; +})(); diff --git a/test/fixtures/vanilla/input/window_var.js b/test/fixtures/vanilla/input/window_var.js new file mode 100644 index 0000000..116faef --- /dev/null +++ b/test/fixtures/vanilla/input/window_var.js @@ -0,0 +1,3 @@ +window.vip_global = function() { + console.log('the coolest'); +}; diff --git a/test/fixtures/vanilla/output/globals/analytics/global-analytics.js b/test/fixtures/vanilla/output/globals/analytics/global-analytics.js new file mode 100644 index 0000000..fec7272 --- /dev/null +++ b/test/fixtures/vanilla/output/globals/analytics/global-analytics.js @@ -0,0 +1,5 @@ +(function() { + window.global_analytics = 'inject thorough analytics solution'; +})(); + +export default undefined; diff --git a/test/fixtures/vanilla/output/globals/window-var.js b/test/fixtures/vanilla/output/globals/window-var.js new file mode 100644 index 0000000..a25904d --- /dev/null +++ b/test/fixtures/vanilla/output/globals/window-var.js @@ -0,0 +1,5 @@ +window.vip_global = function() { + console.log('the coolest'); +}; + +export default undefined; diff --git a/test/models-test.js b/test/models-test.js index 88f3770..3ccb920 100644 --- a/test/models-test.js +++ b/test/models-test.js @@ -263,4 +263,21 @@ describe('migrating models', function(){ }); }); + describe('Moves window var definition into globals directory', function(){ + + it('migrates the files correctly', function(){ + var expectedModel = fixture('globals/window-var.js').split('\n'); + var actualModel = result('globals/window-var.js').split('\n'); + assert.deepEqual(actualModel, expectedModel); + }); + }); + + describe('Moves self executing anonymous functions to globals', function(){ + + it('migrates the files correctly', function(){ + var expectedModel = fixture('globals/analytics/global-analytics.js').split('\n'); + var actualModel = result('globals/analytics/global-analytics.js').split('\n'); + assert.deepEqual(actualModel, expectedModel); + }); + }); });