From 5d28f611b272a09bbec5bd605469874cc61250ac Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 7 Jun 2017 20:26:48 -0600 Subject: [PATCH 1/5] support for targeting new targets --- plugins.js | 91 +++++++++++++++++++++++++++++------------------ webpack.config.js | 15 +++++++- 2 files changed, 71 insertions(+), 35 deletions(-) diff --git a/plugins.js b/plugins.js index f0c31407f..4403f89bb 100644 --- a/plugins.js +++ b/plugins.js @@ -78,7 +78,11 @@ function isInternal(name) { */ function pluginPath(name) { if (isInternal(name)) { - return path.join(__dirname, 'plugins', name); + try { + return resolve.sync(name, {moduleDirectory: 'plugins', basedir: process.cwd()}); + } catch (e) { + return undefined; + } } try { @@ -88,15 +92,8 @@ function pluginPath(name) { } } -/** - * Itterates over the plugins and gets the plugin path's, version, and name. - * - * @param {Array} plugins - * @returns {Array} - */ -function itteratePlugins(plugins) { - return plugins.map((p) => { - let plugin = {}; +class Plugin { + constructor(entry) { // This checks to see if the structure for this entry is an object: // @@ -106,23 +103,47 @@ function itteratePlugins(plugins) { // // "people" // - if (typeof p === 'object') { - plugin.name = Object.keys(p).find((name) => name !== null); - plugin.version = p[plugin.name]; - } else if (typeof p === 'string') { - plugin.name = p; - plugin.version = `file:./plugins/${plugin.name}`; + if (typeof entry === 'object') { + this.name = Object.keys(entry).find((name) => name !== null); + this.version = entry[this.name]; + } else if (typeof entry === 'string') { + this.name = entry; + this.version = `file:./plugins/${this.name}`; } else { - throw new Error(`plugins.json is malformed, refer to PLUGINS.md for formatting, expected a string or an object for a plugin entry, found a ${typeof p}`); + throw new Error(`plugins.json is malformed, refer to PLUGINS.md for formatting, expected a string or an object for a plugin entry, found a ${typeof entry}`); } // Get the path for the plugin. - plugin.path = pluginPath(plugin.name); + this.path = pluginPath(this.name); + } - return plugin; - }); + require() { + if (typeof this.path === 'undefined') { + throw new Error(`plugin '${this.name}' is not local and is not resolvable, plugin reconsiliation may be required`); + } + + try { + this.module = require(this.path); + } catch (e) { + if (e && e.code && e.code === 'MODULE_NOT_FOUND' && isInternal(this.name)) { + console.error(new Error(`plugin '${this.name}' could not be loaded due to missing dependencies, plugin reconsiliation may be required`)); + throw e; + } + + console.error(new Error(`plugin '${this.name}' could not be required from '${this.path}': ${e.message}`)); + throw e; + } + } } +/** + * Itterates over the plugins and gets the plugin path's, version, and name. + * + * @param {Array} plugins + * @returns {Array} + */ +const itteratePlugins = (plugins) => plugins.map((p) => new Plugin(p)); + // Add each plugin folder to the allowed import path so that they can import our // internal dependancies. Object.keys(plugins).forEach((type) => itteratePlugins(plugins[type]).forEach((plugin) => { @@ -139,22 +160,20 @@ Object.keys(plugins).forEach((type) => itteratePlugins(plugins[type]).forEach((p */ class PluginSection { constructor(plugins) { - this.plugins = itteratePlugins(plugins).map((plugin) => { - if (typeof plugin.path === 'undefined') { - throw new Error(`plugin '${plugin.name}' is not local and is not resolvable, plugin reconsiliation may be required`); - } + this.required = false; + this.plugins = itteratePlugins(plugins); + } - try { - plugin.module = require(plugin.path); - } catch (e) { - if (e && e.code && e.code === 'MODULE_NOT_FOUND' && isInternal(plugin.name)) { - console.error(new Error(`plugin '${plugin.name}' could not be loaded due to missing dependencies, plugin reconsiliation may be required`)); - throw e; - } + require() { + if (this.required) { + return; + } + + this.required = true; + this.plugins.forEach((plugin) => { - console.error(new Error(`plugin '${plugin.name}' could not be required from '${plugin.path}': ${e.message}`)); - throw e; - } + // Load the plugin. + plugin.require(); if (isInternal(plugin.name)) { debug(`loading internal plugin '${plugin.name}' from '${plugin.path}'`); @@ -171,6 +190,10 @@ class PluginSection { * available. */ hook(hook) { + + // Load the plugin source if we haven't already. + this.require(); + return this.plugins .filter(({module}) => hook in module) .filter((plugin) => { diff --git a/webpack.config.js b/webpack.config.js index ddd2f9577..d2cd07300 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -10,6 +10,11 @@ const webpack = require('webpack'); // Possibly load the config from the .env file (if there is one). require('dotenv').config(); +const {plugins, PluginManager} = require('./plugins'); + +const pluginManager = new PluginManager(plugins); +const targetPlugins = pluginManager.section('targets').plugins; + let pluginsConfigPath; let envPlugins = path.join(__dirname, 'plugins.env.js'); @@ -61,6 +66,13 @@ const config = { path.join(__dirname, 'client/', `coral-embed-${embed}`, '/src/index') ]; + return entry; + }, {}), targetPlugins.reduce((entry, plugin) => { + entry[`${plugin.name}/bundle`] = [ + 'babel-polyfill', + plugin.path + ]; + return entry; }, {})), output: { @@ -127,7 +139,8 @@ const config = { plugins: [ new LicenseWebpackPlugin({ pattern: /^(MIT|ISC|BSD.*)$/, - addUrl: true + addUrl: true, + suppressErrors: true }), new Copy([ ...buildEmbeds.map((embed) => ({ From a9a24b96c0ae6cea62eb8210fa9109c53f7dd086 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 8 Jun 2017 11:49:36 -0600 Subject: [PATCH 2/5] cleaned up plugins in webpack --- plugins.js | 10 +++++++--- webpack.config.js | 28 ++++++---------------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/plugins.js b/plugins.js index 4403f89bb..fa17027a2 100644 --- a/plugins.js +++ b/plugins.js @@ -10,6 +10,7 @@ const PLUGINS_JSON = process.env.TALK_PLUGINS_JSON; // Add the current path to the module root. amp.addPath(__dirname); +let pluginsPath; let plugins = {}; // Try to parse the plugins.json file, logging out an error if the plugins.json @@ -22,14 +23,16 @@ try { if (PLUGINS_JSON && PLUGINS_JSON.length > 0) { debug('Now using TALK_PLUGINS_JSON environment variable for plugins'); - plugins = require(envPlugins); + pluginsPath = envPlugins; } else if (fs.existsSync(customPlugins)) { debug(`Now using ${customPlugins} for plugins`); - plugins = JSON.parse(fs.readFileSync(customPlugins, 'utf8')); + pluginsPath = customPlugins; } else { debug(`Now using ${defaultPlugins} for plugins`); - plugins = JSON.parse(fs.readFileSync(defaultPlugins, 'utf8')); + pluginsPath = defaultPlugins; } + + plugins = require(pluginsPath); } catch (err) { if (err.code === 'ENOENT') { console.error('plugins.json and plugins.default.json not found, plugins will not be active'); @@ -249,6 +252,7 @@ class PluginManager { module.exports = { plugins, + pluginsPath, PluginManager, isInternal, pluginPath, diff --git a/webpack.config.js b/webpack.config.js index d2cd07300..3973adc6d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,5 +1,4 @@ const path = require('path'); -const fs = require('fs'); const CompressionPlugin = require('compression-webpack-plugin'); const autoprefixer = require('autoprefixer'); const precss = require('precss'); @@ -10,26 +9,11 @@ const webpack = require('webpack'); // Possibly load the config from the .env file (if there is one). require('dotenv').config(); -const {plugins, PluginManager} = require('./plugins'); +const {plugins, pluginsPath, PluginManager} = require('./plugins'); +const manager = new PluginManager(plugins); +const targetPlugins = manager.section('targets').plugins; -const pluginManager = new PluginManager(plugins); -const targetPlugins = pluginManager.section('targets').plugins; - -let pluginsConfigPath; - -let envPlugins = path.join(__dirname, 'plugins.env.js'); -let customPlugins = path.join(__dirname, 'plugins.json'); -let defaultPlugins = path.join(__dirname, 'plugins.default.json'); - -if (process.env.TALK_PLUGINS_JSON && process.env.TALK_PLUGINS_JSON.length > 0) { - pluginsConfigPath = envPlugins; -} else if (fs.existsSync(customPlugins)) { - pluginsConfigPath = customPlugins; -} else { - pluginsConfigPath = defaultPlugins; -} - -console.log(`Using ${pluginsConfigPath} as the plugin configuration path`); +console.log(`Using ${pluginsPath} as the plugin configuration path`); // Edit the build targets and embeds below. @@ -89,7 +73,7 @@ const config = { { loader: 'plugins-loader', test: /\.(json|js)$/, - include: pluginsConfigPath + include: pluginsPath }, { loader: 'babel-loader', @@ -168,7 +152,7 @@ const config = { resolve: { alias: { plugins: path.resolve(__dirname, 'plugins/'), - pluginsConfig: pluginsConfigPath + pluginsConfig: pluginsPath }, modules: [ path.resolve(__dirname, 'plugins'), From 399f1e74584fd1ddaa84a7a13dee075e8aa83d01 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 8 Jun 2017 16:53:22 -0600 Subject: [PATCH 3/5] simplified webpack file --- plugins.js | 1 + webpack.config.js | 100 ++++++++++++++++++++++++++++------------------ 2 files changed, 62 insertions(+), 39 deletions(-) diff --git a/plugins.js b/plugins.js index fa17027a2..dfe3d3312 100644 --- a/plugins.js +++ b/plugins.js @@ -84,6 +84,7 @@ function pluginPath(name) { try { return resolve.sync(name, {moduleDirectory: 'plugins', basedir: process.cwd()}); } catch (e) { + console.warn(e); return undefined; } } diff --git a/webpack.config.js b/webpack.config.js index 3973adc6d..c580a0c13 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -2,6 +2,7 @@ const path = require('path'); const CompressionPlugin = require('compression-webpack-plugin'); const autoprefixer = require('autoprefixer'); const precss = require('precss'); +const _ = require('lodash'); const Copy = require('copy-webpack-plugin'); const LicenseWebpackPlugin = require('license-webpack-plugin'); const webpack = require('webpack'); @@ -15,8 +16,6 @@ const targetPlugins = manager.section('targets').plugins; console.log(`Using ${pluginsPath} as the plugin configuration path`); -// Edit the build targets and embeds below. - const buildTargets = [ 'coral-admin', 'coral-docs' @@ -26,47 +25,16 @@ const buildEmbeds = [ 'stream' ]; +//============================================================================== +// Base Webpack Config +//============================================================================== + const config = { devtool: 'cheap-module-source-map', - entry: Object.assign({}, { - 'embed': [ - 'babel-polyfill', - path.join(__dirname, 'client/coral-embed/src/index') - ] - }, buildTargets.reduce((entry, target) => { - - // Add the entry for the bundle. - entry[`${target}/bundle`] = [ - 'babel-polyfill', - path.join(__dirname, 'client/', target, '/src/index') - ]; - - return entry; - }, {}), buildEmbeds.reduce((entry, embed) => { - - // Add the entry for the bundle. - entry[`embed/${embed}/bundle`] = [ - 'babel-polyfill', - path.join(__dirname, 'client/', `coral-embed-${embed}`, '/src/index') - ]; - - return entry; - }, {}), targetPlugins.reduce((entry, plugin) => { - entry[`${plugin.name}/bundle`] = [ - 'babel-polyfill', - plugin.path - ]; - - return entry; - }, {})), output: { path: path.join(__dirname, 'dist'), publicPath: '/client/', - filename: '[name].js', - - // NOTE: this causes all exports to override the global.Coral, so no more - // than one bundle.js can be included on a page. - library: 'Coral' + filename: '[name].js' }, module: { rules: [ @@ -164,6 +132,10 @@ const config = { } }; +//============================================================================== +// Production configuration overrides +//============================================================================== + if (process.env.NODE_ENV === 'production') { config.plugins.push(new CompressionPlugin({ asset: '[path].gz[query]', @@ -174,4 +146,54 @@ if (process.env.NODE_ENV === 'production') { })); } -module.exports = config; +//============================================================================== +// Entries +//============================================================================== + +// Applies the base configuration to the following entries. +const applyConfig = (entries, root = {}) => _.merge({}, config, { + entry: entries.reduce((entry, {name, path}) => { + entry[name] = [ + 'babel-polyfill', + path + ]; + + return entry; + }, {}) +}, root); + +module.exports = [ + applyConfig([ + + // Load in the root embed. + { + name: 'embed', + path: path.join(__dirname, 'client/coral-embed/src/index') + } + + ], { + output: { + library: 'Coral' + } + }), + applyConfig([ + + // Load in all the targets. + ...buildTargets.map((target) => ({ + name: `${target}/bundle`, + path: path.join(__dirname, 'client/', target, '/src/index') + })), + + // Load in all the embeds. + ...buildEmbeds.map((embed) => ({ + name: `embed/${embed}/bundle`, + path: path.join(__dirname, 'client/', `coral-embed-${embed}`, '/src/index') + })), + + // Load in all the plugin entries. + ...targetPlugins.map(({name, path}) => ({ + name: `${name}/bundle`, + path + })) + ]) +]; From 8d2c1b72472e3d0d3db5c02182a53fd60d1ab99e Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 8 Jun 2017 17:25:27 -0600 Subject: [PATCH 4/5] changed bundle format --- webpack.config.js | 88 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 27 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index c580a0c13..59fe9607a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,4 +1,5 @@ const path = require('path'); +const fs = require('fs'); const CompressionPlugin = require('compression-webpack-plugin'); const autoprefixer = require('autoprefixer'); const precss = require('precss'); @@ -163,37 +164,70 @@ const applyConfig = (entries, root = {}) => _.merge({}, config, { }, root); module.exports = [ + + // Coral Embed + // applyConfig([ + + // // Load in the root embed. + // { + // name: 'embed', + // path: path.join(__dirname, 'client/coral-embed/src/index') + // } + + // ], { + // output: { + // library: 'Coral' + // } + // }), + + // All framework targets/embeds/plugins. applyConfig([ - // Load in the root embed. - { - name: 'embed', - path: path.join(__dirname, 'client/coral-embed/src/index') - } + // // Load in all the targets. + // ...buildTargets.map((target) => ({ + // name: `${target}/bundle`, + // path: path.join(__dirname, 'client/', target, '/src/index') + // })), - ], { - output: { - library: 'Coral' - } - }), - applyConfig([ - - // Load in all the targets. - ...buildTargets.map((target) => ({ - name: `${target}/bundle`, - path: path.join(__dirname, 'client/', target, '/src/index') - })), - - // Load in all the embeds. - ...buildEmbeds.map((embed) => ({ - name: `embed/${embed}/bundle`, - path: path.join(__dirname, 'client/', `coral-embed-${embed}`, '/src/index') - })), + // // Load in all the embeds. + // ...buildEmbeds.map((embed) => ({ + // name: `embed/${embed}/bundle`, + // path: path.join(__dirname, 'client/', `coral-embed-${embed}`, '/src/index') + // })), // Load in all the plugin entries. - ...targetPlugins.map(({name, path}) => ({ - name: `${name}/bundle`, - path - })) + ...targetPlugins.reduce((entries, plugin) => { + + // Introspect the path to find a targets folder. + let folder = path.dirname(plugin.path); + let files = fs.readdirSync(folder); + + // While the folder does not contain the targets folder... + while (!files.includes('targets')) { + + // Try to go up a folder. + folder = path.normalize(path.join(folder, '..')); + + // And as long as we haven't gone too high + if (!(folder.includes(path.join(__dirname, 'node_modules')) || !folder.includes(path.join(__dirname, 'plugins')))) { + throw new Error(`target plugin ${plugin.name} does not have a 'targets' folder`); + } + + files = fs.readdirSync(folder); + } + + // List all targets available in that folder. + folder = path.join(folder, 'targets'); + + let targets = fs.readdirSync(folder); + if (targets.length === 0) { + throw new Error(`target plugin ${plugin.name} has no targets in it's target folder ${folder}`); + } + + return entries.concat(targets.map((target) => ({ + name: `plugin/${plugin.name}/${target}/bundle`, + path: path.join(folder, target, 'index') + }))); + }, []) ]) ]; From 99174565faa5d0c797bc30242909622e3fc6f31b Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 8 Jun 2017 17:32:01 -0600 Subject: [PATCH 5/5] fixes to webpack config (uncommenting) --- webpack.config.js | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 59fe9607a..94ecad27a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -166,34 +166,34 @@ const applyConfig = (entries, root = {}) => _.merge({}, config, { module.exports = [ // Coral Embed - // applyConfig([ + applyConfig([ - // // Load in the root embed. - // { - // name: 'embed', - // path: path.join(__dirname, 'client/coral-embed/src/index') - // } + // Load in the root embed. + { + name: 'embed', + path: path.join(__dirname, 'client/coral-embed/src/index') + } - // ], { - // output: { - // library: 'Coral' - // } - // }), + ], { + output: { + library: 'Coral' + } + }), // All framework targets/embeds/plugins. applyConfig([ // // Load in all the targets. - // ...buildTargets.map((target) => ({ - // name: `${target}/bundle`, - // path: path.join(__dirname, 'client/', target, '/src/index') - // })), + ...buildTargets.map((target) => ({ + name: `${target}/bundle`, + path: path.join(__dirname, 'client/', target, '/src/index') + })), - // // Load in all the embeds. - // ...buildEmbeds.map((embed) => ({ - // name: `embed/${embed}/bundle`, - // path: path.join(__dirname, 'client/', `coral-embed-${embed}`, '/src/index') - // })), + // Load in all the embeds. + ...buildEmbeds.map((embed) => ({ + name: `embed/${embed}/bundle`, + path: path.join(__dirname, 'client/', `coral-embed-${embed}`, '/src/index') + })), // Load in all the plugin entries. ...targetPlugins.reduce((entries, plugin) => {