فهرست منبع

Merge pull request #6 from yuki-takei/feature-plugin2

Feature plugin2
Yuki Takei 9 سال پیش
والد
کامیت
ee492aaa32

+ 1 - 0
.gitignore

@@ -24,6 +24,7 @@ npm-debug.log
 /.awcache
 .webpack.json
 /compiled/
+/tmp/
 
 # Doc #
 /doc/

+ 34 - 0
bin/generate-plugin-definitions-source.js

@@ -0,0 +1,34 @@
+/**
+ * the tool for genetion of plugin definitions source code
+ *
+ * @author Yuki Takei <yuki@weseek.co.jp>
+ */
+const fs = require('graceful-fs');
+const slash = require('slash');
+const swig = require('swig');
+const helpers = require('../config/helpers');
+
+const TEMPLATE = helpers.root('bin/templates/plugin-definitions.js.swig');
+const OUT = helpers.root('tmp/plugins/plugin-definitions.js');
+
+const PluginUtils = require('../lib/plugins/plugin-utils');
+const pluginUtils = new PluginUtils();
+
+// get definitions
+const definitions = pluginUtils.listPluginNames(helpers.root())
+  .map((name) => {
+    return pluginUtils.generatePluginDefinition(name, true);
+  })
+  .map((definition) => {
+    // convert backslash to slash
+    definition.entries = definition.entries.map((entryPath) => {
+      return slash(entryPath);
+    });
+    return definition;
+  });
+
+var compiledTemplate = swig.compileFile(TEMPLATE);
+var code = compiledTemplate({definitions});
+
+// write
+fs.writeFileSync(OUT, code);

+ 17 - 0
bin/templates/plugin-definitions.js.swig

@@ -0,0 +1,17 @@
+/*
+ * !! don't commit this file !!
+ * !!      just revert       !!
+ */
+module.exports = [
+  {% for definition in definitions %}{
+    name: '{{ definition.name }}',
+    meta: require('{{ definition.name }}'),
+    entries: [
+      {% for entryPath in definition.entries %}
+      require('{{ entryPath }}'),
+      {% endfor %}
+    ]
+  },
+  {% endfor %}
+
+]

+ 0 - 3
config/webpack.dev.js

@@ -16,7 +16,6 @@ const AssetsPlugin = require('assets-webpack-plugin');
 const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
 const CopyWebpackPlugin = require('copy-webpack-plugin');
 const DllBundlesPlugin = require('webpack-dll-bundles-plugin').DllBundlesPlugin;
-const LiveReloadPlugin = require('webpack-livereload-plugin');
 
 /*
  * Webpack Constants
@@ -72,8 +71,6 @@ module.exports = function (options) {
         })
       }),
 
-      new LiveReloadPlugin(),
-
       new webpack.NoEmitOnErrorsPlugin(),
 
     ]

+ 4 - 25
lib/plugins/index.js

@@ -1,28 +1,7 @@
-const plugins = {
-  // 'crowi-plugin-X': {
-  //   meta: require('crowi-plugin-X'),
-  //   entries: [
-  //     require('crowi-plugin-X/lib/server-entry')
-  //   ]
-  // },
-}
+const debug = require('debug')('crowi:plugins');
+const PluginService = require('./plugin.service');
 
 module.exports = function(crowi, app) {
-  var debug = require('debug')('crowi:plugins');
-
-  for (var pluginName of Object.keys(plugins)) {
-    var meta = plugins[pluginName].meta;
-    var entries = plugins[pluginName].entries;
-
-    // v1 is deprecated
-
-    // v2
-    if (2 === meta.pluginSchemaVersion) {
-      debug(`import plugin entries for '${pluginName}'`);
-
-      entries.forEach((entry) => {
-        entry(crowi, app);
-      });
-    }
-  }
+  const pluginService = new PluginService(crowi, app);
+  pluginService.loadPlugins();
 }

+ 60 - 0
lib/plugins/plugin-utils.js

@@ -0,0 +1,60 @@
+const path = require('path');
+const fs = require('graceful-fs');
+
+class PluginUtils {
+
+  /**
+   * return a definition objects that has following structure:
+   *
+   * {
+   *   name: 'crowi-plugin-X',
+   *   meta: require('crowi-plugin-X'),
+   *   entries: [
+   *     'crowi-plugin-X/lib/client-entry'
+   *   ]
+   * }
+   *
+   *
+   * @param {string} pluginName
+   * @return
+   * @memberOf PluginService
+   */
+  generatePluginDefinition(name, isForClient = false) {
+    const meta = require(name);
+    const entries = (isForClient) ? meta.clientEntries : meta.serverEntries;
+
+    return {
+      name,
+      meta,
+      entries,
+    }
+  }
+
+  /**
+   * list plugin module names that starts with 'crowi-plugin-'
+   * borrowing from: https://github.com/hexojs/hexo/blob/d1db459c92a4765620343b95789361cbbc6414c5/lib/hexo/load_plugins.js#L17
+   *
+   * @returns
+   *
+   * @memberOf PluginService
+   */
+  listPluginNames(rootDir) {
+    var packagePath = path.join(rootDir, 'package.json');
+
+    // Make sure package.json exists
+    if (!fs.existsSync(packagePath)) {
+      return [];
+    }
+
+    // Read package.json and find dependencies
+    const content = fs.readFileSync(packagePath);
+    const json = JSON.parse(content);
+    const deps = json.dependencies || {};
+    return Object.keys(deps).filter((name) => {
+      // Ignore plugins whose name is not started with "crowi-"
+      return /^crowi-plugin-/.test(name);
+    });
+  }
+}
+
+module.exports = PluginUtils

+ 48 - 0
lib/plugins/plugin.service.js

@@ -0,0 +1,48 @@
+const debug = require('debug')('crowi:plugins:PluginService');
+const PluginUtils = require('./plugin-utils');
+
+class PluginService {
+
+  constructor(crowi, app) {
+    this.crowi = crowi;
+    this.app = app;
+    this.pluginUtils = new PluginUtils();
+  }
+
+  /**
+   * load plugins
+   *
+   * @memberOf PluginService
+   */
+  loadPlugins() {
+    this.pluginUtils.listPluginNames(this.crowi.rootDir)
+      .map((name) => {
+        return this.pluginUtils.generatePluginDefinition(name);
+      })
+      .forEach((definition) => {
+        this.loadPlugin(definition);
+      });
+  }
+
+  loadPlugin(definition) {
+    const meta = definition.meta;
+
+    // v1 is deprecated
+    if (1 === meta.pluginSchemaVersion) {
+      debug('pluginSchemaVersion 1 is deprecated');
+      return;
+    }
+
+    // v2
+    if (2 === meta.pluginSchemaVersion) {
+      debug(`load plugin '${definition.name}'`);
+
+      definition.entries.forEach((entryPath) => {
+        const entry = require(entryPath);
+        entry(this.crowi, this.app);
+      });
+    }
+  }
+}
+
+module.exports = PluginService;

+ 6 - 0
package.json

@@ -58,6 +58,7 @@
     "express-form": "~0.12.0",
     "express-session": "~1.14.0",
     "googleapis": "=12.3.0",
+    "graceful-fs": "^4.1.11",
     "gulp": "~3.9.0",
     "gulp-concat": "~2.6.0",
     "gulp-cssmin": "~0.1.7",
@@ -106,6 +107,7 @@
     "css-loader": "^0.27.1",
     "easy-livereload": "^1.2.0",
     "express-webpack-assets": "0.0.2",
+    "mkdirp": "^0.5.1",
     "mocha": "~2.2.0",
     "node-dev": "^3.1.3",
     "optimize-js-plugin": "0.0.4",
@@ -115,6 +117,7 @@
     "sass-loader": "^6.0.3",
     "sinon": "~1.14.0",
     "sinon-chai": "~2.7.0",
+    "slash": "^1.0.0",
     "style-loader": "^0.13.2",
     "to-string-loader": "^1.1.5",
     "webpack": "2.2.0",
@@ -131,6 +134,9 @@
     "clean:public": "npm run rimraf -- public/js",
     "clean:dll": "npm run rimraf -- dll",
     "clean": "npm cache clean && npm run rimraf -- public/js dll",
+    "generate-plugin-definitions-source": "mkdirp tmp/plugins && node bin/generate-plugin-definitions-source.js",
+    "prebuild:dev": "npm run generate-plugin-definitions-source",
+    "prebuild:prod": "npm run generate-plugin-definitions-source",
     "prestart": "npm run build:prod",
     "rimraf": "rimraf",
     "server:watch": "node-dev app.js",

+ 15 - 13
resource/js/plugin.js

@@ -1,12 +1,3 @@
-const plugins = {
-  // 'crowi-plugin-X': {
-  //   meta: require('crowi-plugin-X'),
-  //   entries: [
-  //     require('crowi-plugin-X/lib/client-entry')
-  //   ]
-  // },
-}
-
 export default class CrowiPlugin {
 
   /**
@@ -18,9 +9,20 @@ export default class CrowiPlugin {
    * @memberof CrowiPlugin
    */
   installAll(crowi, crowiRenderer) {
-    for (let pluginName of Object.keys(plugins)) {
-      let meta = plugins[pluginName].meta;
-      let entries = plugins[pluginName].entries;
+    // import plugin definitions
+    let definitions = [];
+    try {
+      definitions = require('../../tmp/plugins/plugin-definitions');
+    }
+    catch(e) {
+      // TODO show warning
+      // do nothing
+      return;
+    }
+
+    definitions.forEach((definition) => {
+      const meta = definition.meta;
+      const entries = definition.entries;
 
       // v1 is deprecated
 
@@ -30,7 +32,7 @@ export default class CrowiPlugin {
           entry(crowi, crowiRenderer);
         });
       }
-    }
+    });
   }
 
 }