diff --git a/cli/app-defaults.js b/cli/app-defaults.js index 67f8fdac5..0c8184800 100644 --- a/cli/app-defaults.js +++ b/cli/app-defaults.js @@ -6,7 +6,9 @@ function getAppDefaults (projectPath) { var pkg = require(join(projectPath, 'package.json')) var appOptions = pkg.hoodie || {} - appOptions.name = pkg.name + if (!appOptions.name) { + appOptions.name = pkg.name + } return appOptions } diff --git a/cli/hoodie-defaults.js b/cli/hoodie-defaults.js index 72b158e1a..63a0225c3 100644 --- a/cli/hoodie-defaults.js +++ b/cli/hoodie-defaults.js @@ -2,6 +2,7 @@ module.exports = getHoodieDefaults function getHoodieDefaults () { return { + name: undefined, address: '127.0.0.1', port: 8080, data: '.hoodie', @@ -14,6 +15,8 @@ function getHoodieDefaults () { inMemory: false, loglevel: 'warn', url: undefined, - adminPassword: undefined + adminPassword: undefined, + plugins: [], + app: {} } } diff --git a/cli/options.js b/cli/options.js index a86b4d7fe..229506bb0 100644 --- a/cli/options.js +++ b/cli/options.js @@ -1,5 +1,8 @@ module.exports = getCliOptions +var fs = require('fs') +var path = require('path') + var log = require('npmlog') var pick = require('lodash').pick var rc = require('rc') @@ -119,9 +122,17 @@ function getCliOptions (projectPath) { options.address = options.bindAddress } + options.name = defaults.name options.public = webrootLocator(options.public) + options.plugins = defaults.plugins + options.app = defaults.app options.client = defaults.client + // If app has a hoodie folder, add it to the list of plugins + if (fs.existsSync(path.join(projectPath, 'hoodie'))) { + options.plugins.push(projectPath) + } + // rc & yargs are setting keys we are not interested in, like in-memory or _ // so we only pick the relevant ones based on they keys of the default options. return pick(options, Object.keys(hoodieDefaults)) diff --git a/cli/parse-options.js b/cli/parse-options.js index a4f818296..6929226a9 100644 --- a/cli/parse-options.js +++ b/cli/parse-options.js @@ -30,11 +30,14 @@ function parseOptions (options) { var dbOptions = {} var config = { + name: options.name, loglevel: options.loglevel, paths: { data: options.data, public: options.public }, + plugins: options.plugins, + app: options.app, inMemory: Boolean(options.inMemory), client: options.client } diff --git a/server/index.js b/server/index.js index 51deb9929..a12b0647e 100644 --- a/server/index.js +++ b/server/index.js @@ -14,7 +14,9 @@ function register (server, options, next) { _.defaultsDeep(options, { paths: { public: 'public' - } + }, + plugins: [], + app: {} }) server.ext('onPreResponse', corsHeaders) @@ -23,6 +25,7 @@ function register (server, options, next) { if (error) { return next(error) } + registerPlugins(server, options, function (error) { if (error) { return next(error) diff --git a/server/plugins/client/bundle-handler-factory.js b/server/plugins/client/bundle-handler-factory.js index 304b1111c..796c740b8 100644 --- a/server/plugins/client/bundle-handler-factory.js +++ b/server/plugins/client/bundle-handler-factory.js @@ -23,7 +23,6 @@ function createBundleHandler (hoodieClientPath, bundleTargetPath, bundleConfig) }) }) } - bundlePromise.then(function (buffer) { reply(buffer).bytes(buffer.length).type('application/javascript') }).catch(reply) diff --git a/server/plugins/client/bundle.js b/server/plugins/client/bundle.js index 8a31c1a37..dbda65d89 100644 --- a/server/plugins/client/bundle.js +++ b/server/plugins/client/bundle.js @@ -27,10 +27,15 @@ function checkModule (module) { * client, so we need to browserify on-the-fly to avoid dependency duplication, * and avoiding unneeded bundling with browserify saves a significant time. */ -function bundleClient (hoodieClientPath, bundleTargetPath, config, callback) { - var plugins = [ - path.resolve('hoodie/client') - ].filter(checkModule) +function bundleClient (hoodieClientPath, bundleTargetPath, options, callback) { + var pluginPaths = options.plugins.map(function (pluginName) { + return pluginName + '/hoodie/client' + }) + + var plugins = [path.resolve('hoodie/client')] + .concat(pluginPaths) + .filter(checkModule) + var getPluginsModifiedTimes = plugins.map(function (pluginPath) { return getModifiedTime.bind(null, requireResolve(pluginPath)) }) @@ -48,7 +53,7 @@ function bundleClient (hoodieClientPath, bundleTargetPath, config, callback) { var sourceTime = Math.max.apply(null, results) var hasUpdate = sourceTime > targetTime - var get = hasUpdate ? buildBundle.bind(null, config, plugins) : fs.readFile.bind(null, bundleTargetPath) + var get = hasUpdate ? buildBundle.bind(null, options, plugins) : fs.readFile.bind(null, bundleTargetPath) get(function (error, buffer) { if (error) { @@ -71,7 +76,7 @@ function getModifiedTime (path, callback) { }) } -function buildBundle (config, plugins, callback) { +function buildBundle (options, plugins, callback) { var ReadableStream = require('stream').Readable var browserify = require('browserify') var stream = new ReadableStream() @@ -84,14 +89,14 @@ function buildBundle (config, plugins, callback) { hoodieBundleSource += 'var Hoodie = require("@hoodie/client")\n' hoodieBundleSource += 'var options = {\n' - if (config.client) { - Object.keys(config.client).forEach(function (key) { - hoodieBundleSource += ' "' + key + '": ' + JSON.stringify(config.client[key]) + ',\n' + if (options.client) { + Object.keys(options.client).forEach(function (key) { + hoodieBundleSource += ' "' + key + '": ' + JSON.stringify(options.client[key]) + ',\n' }) } - if (config.url) { - hoodieBundleSource += ' url: "' + config.url + '",\n' + if (options.url) { + hoodieBundleSource += ' url: "' + options.url + '",\n' } else { hoodieBundleSource += ' url: location.origin,\n' } diff --git a/server/plugins/client/index.js b/server/plugins/client/index.js index d81d54b26..3b1b9bb79 100644 --- a/server/plugins/client/index.js +++ b/server/plugins/client/index.js @@ -11,7 +11,7 @@ var createBundleHandler = require('./bundle-handler-factory') function register (server, options, next) { var hoodieClientModulePath = path.dirname(require.resolve('@hoodie/client/package.json')) var hoodieClientPath = path.join(hoodieClientModulePath, 'index.js') - var bundleTargetPath = path.join(options.config.data || '.hoodie', 'client.js') + var bundleTargetPath = path.join(options.data || '.hoodie', 'client.js') // TODO: add /hoodie/client.min.js path // https://github.com/hoodiehq/hoodie-client/issues/34 @@ -19,7 +19,7 @@ function register (server, options, next) { server.route([{ method: 'GET', path: '/hoodie/client.js', - handler: createBundleHandler(hoodieClientPath, bundleTargetPath, options.config) + handler: createBundleHandler(hoodieClientPath, bundleTargetPath, options) }]) next() diff --git a/server/plugins/index.js b/server/plugins/index.js index 89f3384c2..e54ac223b 100644 --- a/server/plugins/index.js +++ b/server/plugins/index.js @@ -1,12 +1,11 @@ module.exports = registerPlugins var log = require('npmlog') -var path = require('path') var requireResolve = require('./resolver') -function checkModule (module) { +function checkModule (modulePath) { try { - requireResolve(module) + requireResolve(modulePath) return true } catch (err) { if (err.code !== 'MODULE_NOT_FOUND') { @@ -16,12 +15,9 @@ function checkModule (module) { } } -function registerPlugins (server, config, callback) { - var options = { - config: config - } +function registerPlugins (server, options, callback) { var hapiPlugins = [ - require('inert') + 'inert' ] var localPlugins = [ @@ -30,21 +26,33 @@ function registerPlugins (server, config, callback) { './maybe-force-gzip', './public' ] - .concat( - [ - path.resolve('hoodie/server') - ] - .filter(checkModule) - ) + + var externalPlugins = options.plugins + .filter(function (pluginPath) { + return checkModule(pluginPath + '/hoodie/server') + }) + .map(function (pluginPath) { + var pkg = require(pluginPath + '/package.json') + var pluginName = pkg.hoodie ? pkg.hoodie.name || pkg.name : pkg.name + + return { + register: pluginPath + '/hoodie/server', + routes: { prefix: '/hoodie/' + pluginName } + } + }) + + var plugins = hapiPlugins.concat(localPlugins, externalPlugins) .map(function (register) { + var path = register.register ? register.register : register return { options: options, - register: require(register) + register: require(path), + routes: register.routes } }) log.silly('hapi', 'Registering internal plugins') - server.register(hapiPlugins.concat(localPlugins), function (error) { + server.register(plugins, function (error) { if (error) { return callback(error) } diff --git a/server/plugins/public.js b/server/plugins/public.js index 6c1c76ae5..a0f249eda 100644 --- a/server/plugins/public.js +++ b/server/plugins/public.js @@ -4,11 +4,15 @@ module.exports.register.attributes = { dependencies: 'inert' } +var path = require('path') +var requireResolve = require('./resolver') var createReadStream = require('fs').createReadStream -var pathJoin = require('path').join function register (server, options, next) { - var publicFolder = options.config.paths.public + var paths = options.paths + var plugins = options.plugins + var publicFolder = paths.public + var hoodieVersion try { hoodieVersion = require('hoodie/package.json').version @@ -16,10 +20,9 @@ function register (server, options, next) { hoodieVersion = 'development' } - var hoodiePublicPath = pathJoin(require.resolve('../../package.json'), '..', 'public') - var adminPublicPath = pathJoin(require.resolve('@hoodie/admin/package.json'), '..', 'dist') - - server.route([{ + var hoodiePublicPath = path.join(requireResolve('../../package.json'), '..', 'public') + var adminPublicPath = path.join(requireResolve('@hoodie/admin/package.json'), '..', 'dist') + var routes = [{ method: 'GET', path: '/{p*}', handler: { @@ -55,15 +58,49 @@ function register (server, options, next) { handler: function (request, reply) { reply({ hoodie: true, - name: options.config.name, + name: options.name, version: hoodieVersion }) } - }]) + }] + + // add plugin routes + plugins.forEach(function (pluginPath) { + // check if module directory exists + try { + var pluginPackagePath = requireResolve(pluginPath + '/package.json') + } catch (error) { + if (error.code !== 'MODULE_NOT_FOUND') { + throw error + } + } + + if (!pluginPackagePath) { + return + } + + var pkg = require(pluginPackagePath) + + var name = pkg.hoodie ? pkg.hoodie.name || pkg.name : pkg.name + + routes.push({ + method: 'GET', + path: '/hoodie/' + name + '/{p*}', + handler: { + directory: { + path: path.join(path.dirname(pluginPackagePath), 'hoodie', 'public'), + listing: false, + index: true + } + } + }) + }) + + server.route(routes) // serve app whenever an html page is requested // and no other document is available - var app = pathJoin(publicFolder, 'index.html') + var app = path.join(publicFolder, 'index.html') server.ext('onPostHandler', function (request, reply) { var response = request.response @@ -77,7 +114,7 @@ function register (server, options, next) { var isAdminPublicPath = /^\/hoodie\/admin\//.test(request.path) && !(/^\/hoodie\/admin\/api\//).test(request.path) if (isAdminPublicPath && isHtmlRequest) { - return reply(createReadStream(pathJoin(adminPublicPath, 'index.html'))) + return reply(createReadStream(path.join(adminPublicPath, 'index.html'))) } if (isHoodiePath) {