mirror of
https://github.com/wassname/talk.git
synced 2026-09-12 13:01:11 +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.
|
// Add the current path to the module root.
|
||||||
amp.addPath(__dirname);
|
amp.addPath(__dirname);
|
||||||
|
|
||||||
|
let pluginsPath;
|
||||||
let plugins = {};
|
let plugins = {};
|
||||||
|
|
||||||
// Try to parse the plugins.json file, logging out an error if the plugins.json
|
// 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) {
|
if (PLUGINS_JSON && PLUGINS_JSON.length > 0) {
|
||||||
debug('Now using TALK_PLUGINS_JSON environment variable for plugins');
|
debug('Now using TALK_PLUGINS_JSON environment variable for plugins');
|
||||||
plugins = require(envPlugins);
|
pluginsPath = envPlugins;
|
||||||
} else if (fs.existsSync(customPlugins)) {
|
} else if (fs.existsSync(customPlugins)) {
|
||||||
debug(`Now using ${customPlugins} for plugins`);
|
debug(`Now using ${customPlugins} for plugins`);
|
||||||
plugins = JSON.parse(fs.readFileSync(customPlugins, 'utf8'));
|
pluginsPath = customPlugins;
|
||||||
} else {
|
} else {
|
||||||
debug(`Now using ${defaultPlugins} for plugins`);
|
debug(`Now using ${defaultPlugins} for plugins`);
|
||||||
plugins = JSON.parse(fs.readFileSync(defaultPlugins, 'utf8'));
|
pluginsPath = defaultPlugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugins = require(pluginsPath);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code === 'ENOENT') {
|
if (err.code === 'ENOENT') {
|
||||||
console.error('plugins.json and plugins.default.json not found, plugins will not be active');
|
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) {
|
function pluginPath(name) {
|
||||||
if (isInternal(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 {
|
try {
|
||||||
@@ -88,15 +96,8 @@ function pluginPath(name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
class Plugin {
|
||||||
* Itterates over the plugins and gets the plugin path's, version, and name.
|
constructor(entry) {
|
||||||
*
|
|
||||||
* @param {Array<Object|String>} plugins
|
|
||||||
* @returns {Array<Object>}
|
|
||||||
*/
|
|
||||||
function itteratePlugins(plugins) {
|
|
||||||
return plugins.map((p) => {
|
|
||||||
let plugin = {};
|
|
||||||
|
|
||||||
// This checks to see if the structure for this entry is an object:
|
// This checks to see if the structure for this entry is an object:
|
||||||
//
|
//
|
||||||
@@ -106,23 +107,47 @@ function itteratePlugins(plugins) {
|
|||||||
//
|
//
|
||||||
// "people"
|
// "people"
|
||||||
//
|
//
|
||||||
if (typeof p === 'object') {
|
if (typeof entry === 'object') {
|
||||||
plugin.name = Object.keys(p).find((name) => name !== null);
|
this.name = Object.keys(entry).find((name) => name !== null);
|
||||||
plugin.version = p[plugin.name];
|
this.version = entry[this.name];
|
||||||
} else if (typeof p === 'string') {
|
} else if (typeof entry === 'string') {
|
||||||
plugin.name = p;
|
this.name = entry;
|
||||||
plugin.version = `file:./plugins/${plugin.name}`;
|
this.version = `file:./plugins/${this.name}`;
|
||||||
} else {
|
} 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.
|
// 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
|
// Add each plugin folder to the allowed import path so that they can import our
|
||||||
// internal dependancies.
|
// internal dependancies.
|
||||||
Object.keys(plugins).forEach((type) => itteratePlugins(plugins[type]).forEach((plugin) => {
|
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 {
|
class PluginSection {
|
||||||
constructor(plugins) {
|
constructor(plugins) {
|
||||||
this.plugins = itteratePlugins(plugins).map((plugin) => {
|
this.required = false;
|
||||||
if (typeof plugin.path === 'undefined') {
|
this.plugins = itteratePlugins(plugins);
|
||||||
throw new Error(`plugin '${plugin.name}' is not local and is not resolvable, plugin reconsiliation may be required`);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
require() {
|
||||||
plugin.module = require(plugin.path);
|
if (this.required) {
|
||||||
} catch (e) {
|
return;
|
||||||
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;
|
this.required = true;
|
||||||
}
|
this.plugins.forEach((plugin) => {
|
||||||
|
|
||||||
console.error(new Error(`plugin '${plugin.name}' could not be required from '${plugin.path}': ${e.message}`));
|
// Load the plugin.
|
||||||
throw e;
|
plugin.require();
|
||||||
}
|
|
||||||
|
|
||||||
if (isInternal(plugin.name)) {
|
if (isInternal(plugin.name)) {
|
||||||
debug(`loading internal plugin '${plugin.name}' from '${plugin.path}'`);
|
debug(`loading internal plugin '${plugin.name}' from '${plugin.path}'`);
|
||||||
@@ -171,6 +194,10 @@ class PluginSection {
|
|||||||
* available.
|
* available.
|
||||||
*/
|
*/
|
||||||
hook(hook) {
|
hook(hook) {
|
||||||
|
|
||||||
|
// Load the plugin source if we haven't already.
|
||||||
|
this.require();
|
||||||
|
|
||||||
return this.plugins
|
return this.plugins
|
||||||
.filter(({module}) => hook in module)
|
.filter(({module}) => hook in module)
|
||||||
.filter((plugin) => {
|
.filter((plugin) => {
|
||||||
@@ -226,6 +253,7 @@ class PluginManager {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins,
|
plugins,
|
||||||
|
pluginsPath,
|
||||||
PluginManager,
|
PluginManager,
|
||||||
isInternal,
|
isInternal,
|
||||||
pluginPath,
|
pluginPath,
|
||||||
|
|||||||
+102
-49
@@ -3,6 +3,7 @@ const fs = require('fs');
|
|||||||
const CompressionPlugin = require('compression-webpack-plugin');
|
const CompressionPlugin = require('compression-webpack-plugin');
|
||||||
const autoprefixer = require('autoprefixer');
|
const autoprefixer = require('autoprefixer');
|
||||||
const precss = require('precss');
|
const precss = require('precss');
|
||||||
|
const _ = require('lodash');
|
||||||
const Copy = require('copy-webpack-plugin');
|
const Copy = require('copy-webpack-plugin');
|
||||||
const LicenseWebpackPlugin = require('license-webpack-plugin');
|
const LicenseWebpackPlugin = require('license-webpack-plugin');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
@@ -10,23 +11,11 @@ const webpack = require('webpack');
|
|||||||
// Possibly load the config from the .env file (if there is one).
|
// Possibly load the config from the .env file (if there is one).
|
||||||
require('dotenv').config();
|
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');
|
console.log(`Using ${pluginsPath} as the plugin configuration path`);
|
||||||
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.
|
|
||||||
|
|
||||||
const buildTargets = [
|
const buildTargets = [
|
||||||
'coral-admin',
|
'coral-admin',
|
||||||
@@ -37,47 +26,23 @@ const buildEmbeds = [
|
|||||||
'stream'
|
'stream'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Base Webpack Config
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
devtool: 'cheap-module-source-map',
|
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: {
|
output: {
|
||||||
path: path.join(__dirname, 'dist'),
|
path: path.join(__dirname, 'dist'),
|
||||||
publicPath: '/client/',
|
publicPath: '/client/',
|
||||||
filename: '[name].js',
|
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'
|
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
loader: 'plugins-loader',
|
loader: 'plugins-loader',
|
||||||
test: /\.(json|js)$/,
|
test: /\.(json|js)$/,
|
||||||
include: pluginsConfigPath
|
include: pluginsPath
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
@@ -127,7 +92,8 @@ const config = {
|
|||||||
plugins: [
|
plugins: [
|
||||||
new LicenseWebpackPlugin({
|
new LicenseWebpackPlugin({
|
||||||
pattern: /^(MIT|ISC|BSD.*)$/,
|
pattern: /^(MIT|ISC|BSD.*)$/,
|
||||||
addUrl: true
|
addUrl: true,
|
||||||
|
suppressErrors: true
|
||||||
}),
|
}),
|
||||||
new Copy([
|
new Copy([
|
||||||
...buildEmbeds.map((embed) => ({
|
...buildEmbeds.map((embed) => ({
|
||||||
@@ -156,7 +122,7 @@ const config = {
|
|||||||
alias: {
|
alias: {
|
||||||
'plugin-api': path.resolve(__dirname, 'plugin-api/'),
|
'plugin-api': path.resolve(__dirname, 'plugin-api/'),
|
||||||
plugins: path.resolve(__dirname, 'plugins/'),
|
plugins: path.resolve(__dirname, 'plugins/'),
|
||||||
pluginsConfig: pluginsConfigPath
|
pluginsConfig: pluginsPath
|
||||||
},
|
},
|
||||||
modules: [
|
modules: [
|
||||||
path.resolve(__dirname, 'plugins'),
|
path.resolve(__dirname, 'plugins'),
|
||||||
@@ -168,6 +134,10 @@ const config = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Production configuration overrides
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
config.plugins.push(new CompressionPlugin({
|
config.plugins.push(new CompressionPlugin({
|
||||||
asset: '[path].gz[query]',
|
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