'use strict'; var path = require('path'); var Registry = require('./'); var relativeRequire = require('process-relative-require'); var debug = require('debug')('ember-cli:preprocessors'); /** Invokes the `setupRegistryForEachAddon('parent', registry)` hook for each of the parent objects addons. @private @method setupRegistryForEachAddon @param {Registry} registry the registry being setup @param {Addon|EmberApp} parent the parent object of the registry being setup. Will be an addon for nested addons, or the `EmberApp` for addons in the project directly. */ function setupRegistryForEachAddon(registry, parent) { parent.initializeAddons(); var addons = parent.addons || (parent.project && parent.project.addons); if (!addons) { return; } addons.forEach(function(addon) { if (addon.setupPreprocessorRegistry) { addon.setupPreprocessorRegistry('parent', registry); } }); } /** Invokes the `setupPreprocessorRegistry` hook for a given addon. The `setupPreprocessorRegistry` will be invoked first on the addon itself (with the first argument of `'self'`), and then on each nested addon (with the first argument of `'parent'`). @private @method setupRegistry @param {Addon|EmberApp} */ module.exports.setupRegistry = function(appOrAddon) { var registry = appOrAddon.registry; if (appOrAddon.setupPreprocessorRegistry) { appOrAddon.setupPreprocessorRegistry('self', registry); } setupRegistryForEachAddon(registry, appOrAddon); addLegacyPreprocessors(registry); }; /** Creates a Registry instance, and prepopulates it with a few static default preprocessors. @private @method defaultRegistry @param app */ module.exports.defaultRegistry = function(app) { var registry = new Registry(app.dependencies(), app); return registry; }; /** Add old / grandfathered preprocessor that is not an ember-cli addon. These entries should be removed, once they have good addon replacements. @private @method addLegacyPreprocessors @param registry */ function addLegacyPreprocessors(registry) { registry.add('minify-css', 'broccoli-csso', null); registry.add('template', 'broccoli-emblem-compiler', ['embl', 'emblem']); registry.add('template', 'broccoli-ember-hbs-template-compiler', ['hbs', 'handlebars']); } /** Returns true if the given path would be considered of a specific type. For example: ``` isType('somefile.js', 'js', addon); // => true isType('somefile.css', 'css', addon); // => true isType('somefile.blah', 'css', addon); // => false isType('somefile.sass', 'css', addon); // => true if a sass preprocessor is available ``` @private @method isType @param {String} file the path to check @param {String} type the type to compare with @param {Object} registryOwner the object whose registry we should search */ module.exports.isType = function(file, type, registryOwner) { var extension = path.extname(file).replace('.', ''); if (extension === type) { return true; } if (registryOwner.registry.extensionsForType(type).indexOf(extension) > -1) { return true; } }; module.exports.preprocessMinifyCss = function(tree, options) { var plugins = options.registry.load('minify-css'); if (plugins.length === 0) { var compiler = require('broccoli-clean-css'); return compiler(tree, options); } else if (plugins.length > 1) { throw new Error('You cannot use more than one minify-css plugin at once.'); } var plugin = plugins[0]; return relativeRequire(plugin.name).call(null, tree, options); }; module.exports.preprocessCss = function(tree, inputPath, outputPath, options) { var plugins = options.registry.load('css'); if (plugins.length === 0) { var Funnel = require('broccoli-funnel'); return new Funnel(tree, { srcDir: inputPath, getDestinationPath: function(relativePath) { if (options.outputPaths) { // options.outputPaths is not present when compiling // an addon's styles var path = relativePath.replace(/\.css$/, ''); // is a rename rule present? if (options.outputPaths[path]) { return options.outputPaths[path]; } } return outputPath + '/' + relativePath; } }); } return processPlugins(plugins, arguments); }; module.exports.preprocessTemplates = function(/* tree */) { var options = arguments[arguments.length - 1]; var plugins = options.registry.load('template'); debug('plugins found for templates: %s', plugins.map(function(p) { return p.name; })); if (plugins.length === 0) { throw new Error('Missing template processor'); } return processPlugins(plugins, arguments); }; module.exports.preprocessJs = function(/* tree, inputPath, outputPath, options */) { var options = arguments[arguments.length - 1]; var plugins = options.registry.load('js'); var tree = arguments[0]; if (plugins.length === 0) { return tree; } return processPlugins(plugins, arguments); }; function processPlugins(plugins, args) { args = Array.prototype.slice.call(args); var tree = args.shift(); plugins.forEach(function(plugin) { debug('processing %s', plugin.name); tree = plugin.toTree.apply(plugin, [tree].concat(args)); }); return tree; }