ember-cli
Version:
Command line tool for developing ambitious ember.js apps
350 lines (271 loc) • 9.22 kB
JavaScript
'use strict';
var fs = require('fs');
var path = require('path');
var deprecate = require('../utilities/deprecate');
var assign = require('lodash-node/modern/objects/assign');
var glob = require('glob');
var SilentError = require('../errors/silent');
var reexport = require('../utilities/reexport');
var p = require('../preprocessors');
var preprocessJs = p.preprocessJs;
var preprocessCss = p.preprocessCss;
var preprocessTemplates = p.preprocessTemplates;
var CoreObject = require('core-object');
function Addon(project) {
this.project = project;
this.registry = p.setupRegistry(this);
this._didRequiredBuildPackages = false;
this.treePaths = {
app: 'app',
styles: 'app/styles',
templates: 'app/templates',
addon: 'addon',
'addon-styles': 'addon/styles',
'addon-templates': 'addon/templates',
vendor: 'vendor',
'test-support': 'test-support',
public: 'public'
};
this.treeForMethods = {
app: 'treeForApp',
styles: 'treeForStyles',
templates: 'treeForTemplates',
addon: 'treeForAddon',
vendor: 'treeForVendor',
'test-support': 'treeForTestSupport',
public: 'treeForPublic'
};
if (!this.name) {
throw new SilentError('An addon must define a `name` property.');
}
}
Addon.__proto__ = CoreObject;
Addon.prototype.constructor = Addon;
Addon.prototype._requireBuildPackages = function() {
if (this._didRequiredBuildPackages === true) {
return;
} else {
this._didRequiredBuildPackages = true;
}
this.compileES6 = this.compileES6 || require('broccoli-es6-concatenator');
this.mergeTrees = this.mergeTrees || require('broccoli-merge-trees');
this.jshintTrees = this.jshintTrees|| require('broccoli-jshint');
this.concatFiles = this.concatFiles|| require('broccoli-concat');
this.fileMover = this.fileMover || require('broccoli-file-mover');
this.pickFiles = this.pickFiles || require('../broccoli/custom-static-compiler');
};
Addon.prototype._unwatchedTreeGenerator = function unwatchedTree(dir) {
return {
read: function() { return dir; },
cleanup: function() { }
};
};
Addon.prototype.isDevelopingAddon = function() {
return process.env.EMBER_ADDON_ENV === 'development';
};
Addon.prototype.treeGenerator = function(dir) {
var tree;
if (this.isDevelopingAddon()) {
tree = dir;
} else {
tree = this._unwatchedTreeGenerator(dir);
}
return tree;
};
Addon.prototype.treeFor = function treeFor(name) {
this._requireBuildPackages();
var tree;
var trees = [];
if (tree = this._treeFor(name)) {
trees.push(tree);
}
if (this.isDevelopingAddon() && this.app.hinting && name === 'app') {
trees.push(this.jshintAddonTree());
}
return this.mergeTrees(trees.filter(Boolean));
};
Addon.prototype._treeFor = function _treeFor(name) {
var treePath = path.join(this.root, this.treePaths[name]);
var treeForMethod = this.treeForMethods[name];
var tree;
if (fs.existsSync(treePath)) {
tree = this.treeGenerator(treePath);
if (this[treeForMethod]) {
tree = this[treeForMethod](tree);
}
}
return tree;
};
Addon.prototype.included = function(app) {
this.app = app;
};
Addon.prototype.includedModules = function() {
if (this._includedModules) {
return this._includedModules;
}
var _this = this;
var treePath = path.join(this.root, this.treePaths['addon']).replace(/\\/g, '/');
var modulePath;
this._includedModules = glob.sync(path.join(treePath, this._jsFileGlob())).reduce(function(ignoredModules, filePath) {
modulePath = filePath.replace(treePath, _this.moduleName());
modulePath = modulePath.slice(0, -(path.extname(modulePath).length));
ignoredModules[modulePath] = ['default'];
return ignoredModules;
}, {});
if (this._includedModules[this.moduleName() + '/index']) {
this._includedModules[this.moduleName()] = ['default'];
}
return this._includedModules;
};
Addon.prototype.treeForPublic = function(tree) {
this._requireBuildPackages();
return this.pickFiles(tree, {
srcDir: '/',
destDir: '/' + this.moduleName()
});
};
Addon.prototype.treeForAddon = function(tree) {
this._requireBuildPackages();
var addonTree = this.compileAddon(tree);
var stylesTree = this.compileStyles(this._treeFor('addon-styles'));
var jsTree;
if (addonTree) {
jsTree = this.concatFiles(addonTree, {
inputFiles: ['**/*.js'],
outputFile: '/' + this.name + '.js',
allowNone: true
});
}
return this.mergeTrees([jsTree, stylesTree].filter(Boolean));
};
Addon.prototype.compileStyles = function(tree) {
this._requireBuildPackages();
if (tree) {
var processedStyles = preprocessCss(tree, '/', '/', {
registry: this.registry
});
return this.fileMover(processedStyles, {
srcFile: '/' + this.app.name + '.css',
destFile: '/' + this.name + '.css'
});
}
};
Addon.prototype.compileTemplates = function(tree) {
this._requireBuildPackages();
if (tree) {
var standardTemplates = this.pickFiles(tree, {
srcDir: '/',
destDir: this.name + '/templates'
});
var podTemplates = this.pickFiles(tree, {
srcDir: '/',
files: ['**/template.*'],
destDir: this.name + '/',
allowEmpty: true
});
return preprocessTemplates(this.mergeTrees([standardTemplates, podTemplates]), {
registry: this.registry
});
}
};
Addon.prototype.compileAddon = function(tree) {
this._requireBuildPackages();
if (Object.keys(this.includedModules()).length === 0) {
return;
}
var addonJs = this.addonJsFiles(tree);
var templatesTree = this.compileTemplates(this._treeFor('addon-templates'));
var reexported = reexport(this.name, '__reexport.js');
var trees = [addonJs, templatesTree, reexported].filter(Boolean);
var es6Tree = this.compileES6(this.mergeTrees(trees), {
ignoredModules: Object.keys(this.app.importWhitelist).concat(this.moduleName()),
inputFiles: [this.name + '/**/*.js'],
wrapInEval: this.app.options.wrapInEval,
outputFile: '/' + this.name + '.js',
legacyFilesToAppend: ['__reexport.js']
});
this.app.importWhitelist = assign(this.app.importWhitelist, this.includedModules());
return es6Tree;
};
Addon.prototype.jshintAddonTree = function() {
this._requireBuildPackages();
var addonPath = this.treePaths['addon'];
if (!fs.existsSync(addonPath)) {
return;
}
var addonJs = this.addonJsFiles(addonPath);
var jshintedAddon = this.jshintTrees(addonJs, {
jshintrcPath: this.app.options.jshintrc.app,
description: 'JSHint - Addon'
});
return this.pickFiles(jshintedAddon, {
srcDir: '/',
destDir: this.name + '/tests/'
});
};
Addon.prototype._jsFileGlob = function() {
var extListing = this.registry.extensionsForType('js').join('|');
return '**/*.+(' + extListing + ')';
};
Addon.prototype.addonJsFiles = function(tree) {
this._requireBuildPackages();
var files = this.pickFiles(tree, {
srcDir: '/',
files: [this._jsFileGlob()],
destDir: this.moduleName(),
allowEmpty: true
});
return preprocessJs(files, '/', this.name, {
registry: this.registry
});
};
Addon.prototype.moduleName = function() {
if (!this.modulePrefix) {
this.modulePrefix = (this.modulePrefix || this.name).toLowerCase().replace(/\s/g, '-');
}
return this.modulePrefix;
};
Addon.prototype.blueprintsPath = function() {
var blueprintPath = path.join(this.root, 'blueprints');
if (fs.existsSync(blueprintPath)) {
return blueprintPath;
}
};
Addon.prototype.config = function (env, baseConfig) {
var configPath = path.join(this.root, 'config', 'environment.js');
if (fs.existsSync(configPath)) {
var configGenerator = require(configPath);
return configGenerator(env, baseConfig);
}
};
Addon.resolvePath = function(addon) {
var addonMain;
deprecate(addon.pkg.name + ' is using the deprecated ember-addon-main definition. It should be updated to {\'ember-addon\': {\'main\': \'' + addon.pkg['ember-addon-main'] + '\'}}', addon.pkg['ember-addon-main']);
addonMain = addon.pkg['ember-addon-main'] || addon.pkg['ember-addon'].main || 'index.js';
// Resolve will fail unless it has an extension
if(!path.extname(addonMain)) {
addonMain += '.js';
}
return path.resolve(addon.path, addonMain);
};
Addon.lookup = function(addon) {
var Constructor, addonModule, modulePath, moduleDir;
modulePath = Addon.resolvePath(addon);
moduleDir = path.dirname(modulePath);
if (fs.existsSync(modulePath)) {
addonModule = require(modulePath);
if (typeof addonModule === 'function') {
Constructor = addonModule;
Constructor.prototype.root = Constructor.prototype.root || moduleDir;
} else {
Constructor = Addon.extend(assign({root: moduleDir}, addonModule));
}
} else {
Constructor = Addon.extend({
name: '(generated ' + addon.pkg.name + ' addon)',
root: moduleDir
});
}
return Constructor;
};
module.exports = Addon;