mirror of
https://github.com/wassname/talk.git
synced 2026-06-29 07:42:02 +08:00
Merge branch 'master' into asset-improvements
This commit is contained in:
+65
-37
@@ -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
@@ -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')
|
||||
})));
|
||||
}, [])
|
||||
])
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user