Merge branch 'master' into asset-improvements

This commit is contained in:
Belén Curcio
2017-06-12 14:34:20 -03:00
committed by GitHub
2 changed files with 167 additions and 86 deletions
+65 -37
View File
@@ -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');
@@ -78,7 +81,12 @@ 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) {
console.warn(e);
return undefined;
}
}
try {
@@ -88,15 +96,8 @@ function pluginPath(name) {
}
}
/**
* Itterates over the plugins and gets the plugin path's, version, and name.
*
* @param {Array<Object|String>} plugins
* @returns {Array<Object>}
*/
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 +107,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<Object|String>} plugins
* @returns {Array<Object>}
*/
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 +164,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 +194,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) => {
@@ -226,6 +253,7 @@ class PluginManager {
module.exports = {
plugins,
pluginsPath,
PluginManager,
isInternal,
pluginPath,
+102 -49
View File
@@ -3,6 +3,7 @@ const fs = require('fs');
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');
@@ -10,23 +11,11 @@ const webpack = require('webpack');
// Possibly load the config from the .env file (if there is one).
require('dotenv').config();
let pluginsConfigPath;
const {plugins, pluginsPath, PluginManager} = require('./plugins');
const manager = new PluginManager(plugins);
const targetPlugins = manager.section('targets').plugins;
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`);
// Edit the build targets and embeds below.
console.log(`Using ${pluginsPath} as the plugin configuration path`);
const buildTargets = [
'coral-admin',
@@ -37,47 +26,23 @@ 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;
}, {})),
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: [
{
loader: 'plugins-loader',
test: /\.(json|js)$/,
include: pluginsConfigPath
include: pluginsPath
},
{
loader: 'babel-loader',
@@ -127,7 +92,8 @@ const config = {
plugins: [
new LicenseWebpackPlugin({
pattern: /^(MIT|ISC|BSD.*)$/,
addUrl: true
addUrl: true,
suppressErrors: true
}),
new Copy([
...buildEmbeds.map((embed) => ({
@@ -156,7 +122,7 @@ const config = {
alias: {
'plugin-api': path.resolve(__dirname, 'plugin-api/'),
plugins: path.resolve(__dirname, 'plugins/'),
pluginsConfig: pluginsConfigPath
pluginsConfig: pluginsPath
},
modules: [
path.resolve(__dirname, 'plugins'),
@@ -168,6 +134,10 @@ const config = {
}
};
//==============================================================================
// Production configuration overrides
//==============================================================================
if (process.env.NODE_ENV === 'production') {
config.plugins.push(new CompressionPlugin({
asset: '[path].gz[query]',
@@ -178,4 +148,87 @@ 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 = [
// 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 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.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')
})));
}, [])
])
];