From 4334c3deb6000d571d5fd86c1e14117d4a16f92a Mon Sep 17 00:00:00 2001 From: Harry Wolff Date: Wed, 26 Feb 2014 23:45:45 -0500 Subject: [PATCH] Move Ember Admin to use es6 modules - adds required dependencies to package.json and to bower.json - added required Grunt tasks to transpile and concat ember admin files --- .gitignore | 1 + .npmignore | 1 + Gruntfile.js | 63 ++++-- bower.json | 4 + core/client/app.js | 8 +- core/client/router.js | 14 +- core/client/templates/index.hbs | 1 + core/server/views/default-ember.hbs | 6 +- .../ember-resolver/dist/ember-resolver.js | 211 ++++++++++++++++++ core/shared/vendor/loader.js | 75 +++++++ package.json | 2 + 11 files changed, 362 insertions(+), 24 deletions(-) create mode 100755 core/client/templates/index.hbs create mode 100644 core/shared/vendor/ember-resolver/dist/ember-resolver.js create mode 100755 core/shared/vendor/loader.js diff --git a/.gitignore b/.gitignore index 9cdab09a61..0d6d3b803e 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ projectFilesBackup .build .dist +.tmp /core/clientold/tpl/hbs-tpl.js /core/clientold/assets/css diff --git a/.npmignore b/.npmignore index 14da2fe851..cfa7144e9f 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,7 @@ !** .build .dist +.tmp docs/** _site/** content/images/** diff --git a/Gruntfile.js b/Gruntfile.js index d3df91718b..2eae8aa086 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -58,6 +58,12 @@ var path = require('path'), files: 'core/client/templates/**/*.hbs', tasks: ['emberTemplates'] }, + ember: { + files: [ + 'core/client/**/*.js' + ], + tasks: ['transpile', 'concat_sourcemap'] + }, sass: { files: ['<%= paths.adminOldAssets %>/sass/**/*'], tasks: ['sass:admin'] @@ -72,12 +78,6 @@ var path = require('path'), ], tasks: ['concat'] }, - 'concat-ember': { - files: [ - 'core/client/**/*.js' - ], - tasks: ['concat:dev-ember'] - }, livereload: { files: [ // Theme CSS @@ -311,9 +311,13 @@ var path = require('path'), // ### Config for grunt-ember-templates // Compiles handlebar templates for ember emberTemplates: { - compile: { + dev: { options: { - templateBasePath: /core\/client\/templates/ + templateBasePath: /core\/client\//, + templateFileExtensions: /\.hbs/, + templateRegistration: function (name, template) { + return grunt.config.process("define('ghost/") + name + "', ['exports'], function(__exports__){ __exports__['default'] = " + template + "; });"; + } }, files: { "core/built/scripts/templates-ember.js": "core/client/templates/**/*.hbs" @@ -321,6 +325,35 @@ var path = require('path'), } }, + // ### Config for grunt-es6-module-transpiler + // Compiles Ember es6 modules + transpile: { + client: { + type: 'amd', + moduleName: function (path) { + return 'ghost/' + path; + }, + files: [{ + expand: true, + cwd: 'core/client/', + src: ['**/*.js'], + dest: '.tmp/ember-transpiled/' + }] + } + }, + + // ### Config for grunt-es6-module-transpiler + // Compiles Ember es6 modules + concat_sourcemap: { + client: { + src: ['.tmp/ember-transpiled/**/*.js'], + dest: 'core/built/scripts/ghost-dev-ember.js', + options: { + sourcesContent: true + } + } + }, + // ### Config for grunt-groc // Generate documentation from code groc: { @@ -441,13 +474,11 @@ var path = require('path'), 'dev-ember': { files: { 'core/built/scripts/vendor-ember.js': [ + 'core/shared/vendor/loader.js', 'core/shared/vendor/jquery/jquery.js', 'core/shared/vendor/handlebars/handlebars.js', - 'core/shared/vendor/ember/ember.js' - ], - - 'core/built/scripts/ghost-dev-ember.js': [ - 'core/client/**/*.js' + 'core/shared/vendor/ember/ember.js', + 'core/shared/vendor/ember-resolver/dist/ember-resolver.js' ] } }, @@ -830,6 +861,7 @@ var path = require('path'), 'sass:admin', 'handlebars', 'concat', + 'emberBuild', 'express:dev', 'watch' ]); @@ -875,8 +907,11 @@ var path = require('path'), // Before running in production mode grunt.registerTask('prod', 'Build CSS, JS & templates for production', ['sass:compress', 'handlebars', 'concat', 'uglify']); + // All tasks related to building the Ember client code + grunt.registerTask('emberBuild', 'Build Ember JS & templates for development', ['emberTemplates:dev', 'transpile', 'concat_sourcemap']); + // When you just say 'grunt' - grunt.registerTask('default', 'Build CSS, JS & templates for development', ['update_submodules', 'sass:compress', 'handlebars', 'emberTemplates:compile', 'concat']); + grunt.registerTask('default', 'Build CSS, JS & templates for development', ['update_submodules', 'sass:compress', 'handlebars', 'concat', 'emberBuild']); }; module.exports = configureGrunt; diff --git a/bower.json b/bower.json index f07fdfc27b..8580aeb7d6 100644 --- a/bower.json +++ b/bower.json @@ -2,6 +2,10 @@ "name": "ghost", "dependencies": { "handlebars": "~1.1.2", + "ember": "~1.4.0", + "ember-resolver": "git://github.com/stefanpenner/ember-jj-abrams-resolver.git#9805033c178e7f857f801359664adb599444b430" + }, + "resolutions": { "ember": "~1.4.0" } } diff --git a/core/client/app.js b/core/client/app.js index b4c353615a..56c0cb68d1 100755 --- a/core/client/app.js +++ b/core/client/app.js @@ -1,6 +1,8 @@ /*global Ember */ -var App = Ember.Application.create({ +import Resolver from 'ember/resolver'; + +var App = Ember.Application.extend({ /** * These are debugging flags, they are useful during development */ @@ -9,6 +11,8 @@ var App = Ember.Application.create({ LOG_TRANSITIONS: true, LOG_TRANSITIONS_INTERNAL: true, LOG_VIEW_LOOKUPS: true, - rootElement: '#ember-app' // tells ember to inject this app into element with selector #ember-app + modulePrefix: 'ghost', // TODO: loaded via config + Resolver: Resolver['default'] }); +export default App; diff --git a/core/client/router.js b/core/client/router.js index 1133384526..76a432d973 100755 --- a/core/client/router.js +++ b/core/client/router.js @@ -1,9 +1,11 @@ -/*global App */ +/*global Ember */ -App.Router.map(function () { +// ensure we don't share routes between all Router instances +var Router = Ember.Router.extend(); + +Router.map(function () { 'use strict'; - this.resource('posts'); - this.resource('post', {path: 'post/:id'}, function () { - this.route('edit'); - }); + }); + +export default Router; diff --git a/core/client/templates/index.hbs b/core/client/templates/index.hbs new file mode 100755 index 0000000000..b19c2990f7 --- /dev/null +++ b/core/client/templates/index.hbs @@ -0,0 +1 @@ +This is the index route \ No newline at end of file diff --git a/core/server/views/default-ember.hbs b/core/server/views/default-ember.hbs index 1fc08b3dc3..0ce2ef6d0e 100644 --- a/core/server/views/default-ember.hbs +++ b/core/server/views/default-ember.hbs @@ -32,10 +32,12 @@ -
- + + diff --git a/core/shared/vendor/ember-resolver/dist/ember-resolver.js b/core/shared/vendor/ember-resolver/dist/ember-resolver.js new file mode 100644 index 0000000000..ec86a9a5b6 --- /dev/null +++ b/core/shared/vendor/ember-resolver/dist/ember-resolver.js @@ -0,0 +1,211 @@ +// ========================================================================== +// Project: Ember - JavaScript Application Framework +// Copyright: Copyright 2013 Stefan Penner and Ember App Kit Contributors +// License: Licensed under MIT license +// See https://raw.github.com/stefanpenner/ember-jj-abrams-resolver/master/LICENSE +// ========================================================================== + + + // Version: 0.0.1 + +(function() { +/*globals define registry requirejs */ + +define("ember/resolver", + [], + function() { + "use strict"; + /* + * This module defines a subclass of Ember.DefaultResolver that adds two + * important features: + * + * 1) The resolver makes the container aware of es6 modules via the AMD + * output. The loader's _seen is consulted so that classes can be + * resolved directly via the module loader, without needing a manual + * `import`. + * 2) is able provide injections to classes that implement `extend` + * (as is typical with Ember). + */ + + function classFactory(klass) { + return { + create: function (injections) { + if (typeof klass.extend === 'function') { + return klass.extend(injections); + } else { + return klass; + } + } + }; + } + + var underscore = Ember.String.underscore; + var classify = Ember.String.classify; + var get = Ember.get; + + function parseName(fullName) { + /*jshint validthis:true */ + + var nameParts = fullName.split(":"), + type = nameParts[0], fullNameWithoutType = nameParts[1], + name = fullNameWithoutType, + namespace = get(this, 'namespace'), + root = namespace; + + return { + fullName: fullName, + type: type, + fullNameWithoutType: fullNameWithoutType, + name: name, + root: root, + resolveMethodName: "resolve" + classify(type) + }; + } + + function chooseModuleName(seen, moduleName) { + var underscoredModuleName = Ember.String.underscore(moduleName); + + if (moduleName !== underscoredModuleName && seen[moduleName] && seen[underscoredModuleName]) { + throw new TypeError("Ambiguous module names: `" + moduleName + "` and `" + underscoredModuleName + "`"); + } + + if (seen[moduleName]) { + return moduleName; + } else if (seen[underscoredModuleName]) { + return underscoredModuleName; + } else { + var parts = moduleName.split('/'), + lastPart = parts[parts.length - 1], + partializedModuleName; + + parts[parts.length - 1] = lastPart.replace(/^-/, '_'); + partializedModuleName = parts.join('/'); + + if (seen[partializedModuleName]) { + Ember.deprecate('Modules should not contain underscores. ' + + 'Attempted to lookup "'+moduleName+'" which ' + + 'was not found. Please rename "'+partializedModuleName+'" '+ + 'to "'+moduleName+'" instead.', false); + + return partializedModuleName; + } else { + return moduleName; + } + } + } + + function logLookup(found, parsedName, moduleName) { + if (Ember.ENV.LOG_MODULE_RESOLVER) { + var symbol; + + if (found) { symbol = '[✓]'; } + else { symbol = '[ ]'; } + + Ember.Logger.info(symbol, parsedName.fullName, new Array(40 - parsedName.fullName.length).join('.'), moduleName); + } + } + + function resolveOther(parsedName) { + /*jshint validthis:true */ + + var moduleName, tmpModuleName, prefix, podPrefix, moduleRegistry; + + prefix = this.namespace.modulePrefix; + podPrefix = this.namespace.podModulePrefix || prefix; + moduleRegistry = requirejs._eak_seen; + + Ember.assert('module prefix must be defined', prefix); + + var pluralizedType = parsedName.type + 's'; + var name = parsedName.fullNameWithoutType; + + // lookup using POD formatting first + tmpModuleName = podPrefix + '/' + name + '/' + parsedName.type; + if (moduleRegistry[tmpModuleName]) { + moduleName = tmpModuleName; + } + + // if not using POD format, use the custom prefix + if (this.namespace[parsedName.type + 'Prefix']) { + prefix = this.namespace[parsedName.type + 'Prefix']; + } + + // if router:main or adapter:main look for a module with just the type first + tmpModuleName = prefix + '/' + parsedName.type; + if (!moduleName && name === 'main' && moduleRegistry[tmpModuleName]) { + moduleName = prefix + '/' + parsedName.type; + } + + // fallback if not type:main or POD format + if (!moduleName) { moduleName = prefix + '/' + pluralizedType + '/' + name; } + + // allow treat all dashed and all underscored as the same thing + // supports components with dashes and other stuff with underscores. + var normalizedModuleName = chooseModuleName(moduleRegistry, moduleName); + + if (moduleRegistry[normalizedModuleName]) { + var module = require(normalizedModuleName, null, null, true /* force sync */); + + if (module && module['default']) { module = module['default']; } + + if (module === undefined) { + throw new Error(" Expected to find: '" + parsedName.fullName + "' within '" + normalizedModuleName + "' but got 'undefined'. Did you forget to `export default` within '" + normalizedModuleName + "'?"); + } + + if (this.shouldWrapInClassFactory(module, parsedName)) { + module = classFactory(module); + } + + logLookup(true, parsedName, moduleName); + + return module; + } else { + logLookup(false, parsedName, moduleName); + + return this._super(parsedName); + } + } + // Ember.DefaultResolver docs: + // https://github.com/emberjs/ember.js/blob/master/packages/ember-application/lib/system/resolver.js + var Resolver = Ember.DefaultResolver.extend({ + resolveTemplate: resolveOther, + resolveOther: resolveOther, + makeToString: function(factory, fullName) { + return '' + this.namespace.modulePrefix + '@' + fullName + ':'; + }, + parseName: parseName, + shouldWrapInClassFactory: function(module, parsedName){ + return false; + }, + normalize: function(fullName) { + // replace `.` with `/` in order to make nested controllers work in the following cases + // 1. `needs: ['posts/post']` + // 2. `{{render "posts/post"}}` + // 3. `this.render('posts/post')` from Route + var split = fullName.split(':'); + if (split.length > 1) { + return split[0] + ':' + Ember.String.dasherize(split[1].replace(/\./g, '/')); + } else { + return fullName; + } + } + }); + + Resolver['default'] = Resolver; + return Resolver; +}); + +define("resolver", + ["ember/resolver"], + function (Resolver) { + Ember.deprecate('Importing/requiring Ember Resolver as "resolver" is deprecated, please use "ember/resolver" instead'); + return Resolver; + }); +})(); + + + +(function() { + +})(); + diff --git a/core/shared/vendor/loader.js b/core/shared/vendor/loader.js new file mode 100755 index 0000000000..02ed6f8a78 --- /dev/null +++ b/core/shared/vendor/loader.js @@ -0,0 +1,75 @@ +var define, requireModule, require, requirejs; + +(function() { + var registry = {}, seen = {}, state = {}; + var FAILED = false; + + define = function(name, deps, callback) { + registry[name] = { + deps: deps, + callback: callback + }; + }; + + requirejs = require = requireModule = function(name) { + if (state[name] !== FAILED && + seen.hasOwnProperty(name)) { + return seen[name]; + } + + if (!registry.hasOwnProperty(name)) { + throw new Error('Could not find module ' + name); + } + + var mod = registry[name]; + var deps = mod.deps; + var callback = mod.callback; + var reified = []; + var exports; + var value; + var loaded = false; + + seen[name] = { }; // enable run-time cycles + + try { + for (var i=0, l=deps.length; i