Link legacy frontends in the same fashion

pull/6376/head
Oliver Günther 6 years ago
parent 614f6d2400
commit 2ab1a51d7a
No known key found for this signature in database
GPG Key ID: 88872239EB414F99
  1. 32
      app/assets/javascripts/legacy-application.js
  2. 2
      app/helpers/angular_helper.rb
  3. 60
      bin/plugin_info
  4. 2
      config/initializers/assets.rb
  5. 1
      frontend/.gitignore
  6. 4
      frontend/legacy/app/openproject-legacy-app.ts
  7. 0
      frontend/legacy/postcss.config.js
  8. 75
      frontend/legacy/rails-plugins.conf.js
  9. 1
      frontend/legacy/tsconfig.json
  10. 62
      frontend/legacy/webpack.config.js
  11. 5
      frontend/src/app/modules/plugins/linked-plugins.module.ts
  12. 1
      frontend/tests/typings/tests.d.ts
  13. 38
      frontend/tests/unit/lib/i18n-js.shim.js
  14. 147
      frontend/webpack-ckeditor-config.js
  15. 340
      frontend/webpack-main-config.js
  16. 45
      frontend/webpack-test-config.js
  17. 104
      frontend/webpack-vendors-config.js
  18. 43
      frontend/webpack.config.js
  19. 33
      frontend/webpack.production.config.js
  20. 22
      frontend/webpack/typescript-disruptor.plugin.js
  21. 75
      lib/open_project/plugins/frontend_linking.rb
  22. 155
      lib/open_project/plugins/frontend_linking/generator.rb
  23. 2
      lib/tasks/assets.rake
  24. 4
      lib/tasks/plugins.rake

@ -1,32 +0,0 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
// Contains the legacy bundle from core and plugins
//= require bundles/openproject-core-app
//= require_tree ./bundles

@ -37,7 +37,7 @@ module AngularHelper
def activate_angular_js(type = :div, options = {}, &block)
content_for(:header_tags) do
javascript_include_tag 'legacy-application'
javascript_include_tag 'bundles/openproject-legacy-app'
end
if block_given?

@ -1,60 +0,0 @@
#!/usr/bin/env ruby
#
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'bundler'
require 'json'
DEFAULT_GROUPS = [:opf_plugins]
DEFAULT_GEMFILE = File.expand_path('../../Gemfile', __FILE__)
def plugin_names(gemfile = DEFAULT_GEMFILE)
bundler_groups = ENV['BUNDLER_GROUPS']
groups = bundler_groups.split(',').each_with_object([]) { |group, l| l << group.to_sym } if bundler_groups
groups = DEFAULT_GROUPS if groups.nil?
gems = Bundler::Dsl.evaluate(gemfile, 'foo', true)
gems.dependencies.each_with_object({}) do |dep, l|
l[dep.name] = dep.inspect if (groups & dep.groups).any?
end
end
def plugin_name_paths
op_dep = plugin_names
Bundler.load.specs.each_with_object({}) do |spec, h|
if op_dep.include?(spec.name)
options = spec.source.options
h[spec.name] = spec.full_gem_path
end
end
end
puts JSON.generate(plugin_name_paths)

@ -28,6 +28,6 @@ OpenProject::Application.configure do
types_checkboxes.js
work_packages.js
vendor/ckeditor/ckeditor.*js
legacy-application.js
bundles/openproject-legacy-app.js
)
end

@ -16,3 +16,4 @@
/out-tsc
/generated_typings
/src/app/modules/plugins/linked/
/legacy/app/plugins/*

@ -97,3 +97,7 @@ requireComponent.keys().forEach(requireComponent);
var requireServices = require.context('./services/', true, /^((?!\.(test|spec)).)*\.(js|ts|html)$/);
requireServices.keys().forEach(requireServices);
// Load all dynamically linked plugins
var requirePlugins = require.context('./plugins/', true, /^((?!\.(test|spec)).)*\.(js|ts|html)$/);
requirePlugins.keys().forEach(requirePlugins);

@ -1,75 +0,0 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2017 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See docs/COPYRIGHT.rdoc for more details.
//++
/*global exec */
/*global test */
require('shelljs/global');
var path = require('path'),
_ = require('lodash');
var PLUGIN_INFO_CMD_PATH = path.join(__dirname, '..', '..', 'bin', 'plugin_info');
function runPluginsInfo() {
var currentWorkingDir = process.cwd();
// Make sure we're in the root directory to launch the plugin_info script
process.chdir(path.join(__dirname, '..'));
var fullCmd = exec(PLUGIN_INFO_CMD_PATH, { silent: false });
process.chdir(currentWorkingDir);
return fullCmd.code === 0 ? fullCmd.output : '{}';
}
var OpenProjectPlugins = {
allPluginNamesPaths: _.memoize(function() {
return JSON.parse(runPluginsInfo());
}),
pluginNamesPaths: function() {
return _.reduce(this.allPluginNamesPaths(), function(obj, pluginPath, pluginName) {
if (test('-e', path.join(pluginPath, 'package.json'))) {
obj[pluginName] = pluginPath;
}
return obj;
}, {});
},
findPluginPath: _.memoize(function(name) {
return this.pluginNamesPaths()[name];
}, _.identity),
pluginDirectories: function() {
return _.reduce(this.allPluginNamesPaths(), function(dirList, pluginPath) {
var pluginDir = path.dirname(pluginPath);
return dirList.indexOf(pluginDir) === -1 ? dirList.concat(pluginDir) : dirList;
}, []);
}
};
exports.allPluginNamesPaths = OpenProjectPlugins.allPluginNamesPaths();
exports.pluginNamesPaths = OpenProjectPlugins.pluginNamesPaths();
exports.pluginDirectories = OpenProjectPlugins.pluginDirectories();

@ -21,6 +21,7 @@
"noFallthroughCasesInSwitch": true,
"strictNullChecks": true,
"skipLibCheck": true,
"preserveSymlinks": true,
"baseUrl": ".",
"typeRoots": [
"../node_modules/@types",

@ -27,15 +27,12 @@
// ++
var webpack = require('webpack');
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var pathConfig = require('./rails-plugins.conf');
var CleanWebpackPlugin = require('clean-webpack-plugin');
var MiniCssExtractPlugin = require('mini-css-extract-plugin');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
// var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
var mode = 'production';
var production = true;
@ -50,24 +47,9 @@ var node_root = path.resolve(__dirname, '..', 'node_modules');
var output_root = path.resolve(__dirname, '..', '..', 'app', 'assets', 'javascripts');
var bundle_output = path.resolve(output_root, 'bundles');
var pluginEntries = _.reduce(pathConfig.pluginNamesPaths, function (entries, pluginPath, name) {
entries[name.replace(/^openproject\-/, '')] = path.resolve(pluginPath, 'frontend', 'app', name + '-app.js');
return entries;
}, {});
var pluginAliases = _.reduce(pathConfig.pluginNamesPaths, function (entries, pluginPath, name) {
entries[name] = path.basename(pluginPath);
return entries;
}, {});
var loaders = [
{
test: /\.tsx?$/,
include: [
path.resolve(__dirname, '..', 'common'),
path.resolve(__dirname, 'app'),
path.resolve(__dirname, 'tests')
].concat(_.values(pathConfig.pluginNamesPaths)),
use: [
{
loader: 'ts-loader',
@ -108,31 +90,8 @@ var loaders = [
},
];
for (var k in pathConfig.pluginNamesPaths) {
if (pathConfig.pluginNamesPaths.hasOwnProperty(k)) {
loaders.push({
test: new RegExp('templates\/plugin-' + k.replace(/^openproject\-/, '') + '/.*\.html$'),
use: [
{
loader: 'ngtemplate-loader',
options: {
module: 'OpenProjectLegacy',
relativeTo: path.join(pathConfig.pluginNamesPaths[k], 'frontend', 'app')
}
},
{
loader: 'html-loader',
options: {
minimize: false
}
}
]
});
}
}
loaders.push({
test: /^((?!templates\/plugin).)*\.html$/,
test: /\.html$/,
use: [
{
loader: 'ngtemplate-loader',
@ -150,17 +109,17 @@ loaders.push({
]
});
function getWebpackMainConfig() {
config = {
function getLegacyWebpackConfig() {
var config = {
mode: mode,
devtool: 'source-map',
context: path.resolve(__dirname, 'app'),
entry: _.merge({
'core-app': './openproject-legacy-app'
}, pluginEntries),
entry: {
'legacy-app': './openproject-legacy-app'
},
output: {
filename: 'openproject-[name].js',
@ -173,6 +132,9 @@ function getWebpackMainConfig() {
},
resolve: {
// Resolve symlinks from dynamically linked plugins
symlinks: true,
modules: [
'node_modules',
],
@ -182,12 +144,12 @@ function getWebpackMainConfig() {
// Allow empty import without extension
// enforceExtension: true,
alias: _.merge({
alias: {
'angular': path.resolve(node_root, 'angular', 'angular.min.js'),
'angular-dragula': path.resolve(node_root, 'angular-dragula', 'dist', 'angular-dragula.min.js'),
'core-app': path.resolve(__dirname, 'app'),
'core-components': path.resolve(__dirname, 'app', 'components'),
}, pluginAliases)
}
},
externals: {
@ -261,4 +223,4 @@ function getWebpackMainConfig() {
return config;
}
module.exports = getWebpackMainConfig;
module.exports = getLegacyWebpackConfig;

@ -30,11 +30,12 @@
// rake openproject:plugins:register_frontend
import {NgModule} from "@angular/core";
import OpenprojectCosts from './linked/openproject-costs/main';
@NgModule({
imports: [
OpenprojectCosts,
],
})
export class LinkedPluginsModule { }

@ -1 +0,0 @@
declare var expect: Chai.ExpectStatic;

@ -1,38 +0,0 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2017 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See docs/COPYRIGHT.rdoc for more details.
//++
var I18n = {
t: function(key) {
return '[missing "' + key + '" translation]';
},
defaultLocale: 'en',
locale: 'en',
translations: {
en: {}
}
};

@ -1,147 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
var webpack = require('webpack');
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var autoprefixer = require('autoprefixer');
const CKEditorWebpackPlugin = require( '@ckeditor/ckeditor5-dev-webpack-plugin' );
var CleanWebpackPlugin = require('clean-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var mode = (process.env['RAILS_ENV'] || 'production').toLowerCase();
var uglify = (mode !== 'development');
var node_root = path.resolve(__dirname, 'node_modules');
var output_root = path.resolve(__dirname, '..', 'app', 'assets', 'javascripts');
var bundle_output = path.resolve(output_root, 'editor')
function getWebpackCKEConfig() {
config = {
entry: {
ckeditor: [path.resolve(__dirname, 'ckeditor', 'ckeditor.ts')]
},
module: {
rules: [
{
test: /\.tsx?$/,
include: [
path.resolve(__dirname, 'ckeditor'),
path.resolve(__dirname, 'app'),
],
use: [
{
loader: 'ts-loader',
options: {
logLevel: 'info',
configFile: path.resolve(__dirname, 'ckeditor', 'tsconfig.json')
}
}
]
},
{
// Or /ckeditor5-[^/]+\/theme\/icons\/[^/]+\.svg$/ if you want to limit this loader
// to CKEditor 5's icons only.
test: /\.svg$/,
use: [ 'raw-loader' ]
},
{
// Or /ckeditor5-[^/]+\/theme\/[^/]+\.scss$/ if you want to limit this loader
// to CKEditor 5's theme only.
test: /\.css$/,
use: [
'style-loader',
{
loader: 'postcss-loader',
options: {
config: { path: path.resolve(__dirname, 'ckeditor', 'postcss.config.js') }
}
}
]
},
{
// Or /ckeditor5-[^/]+\/theme\/[^/]+\.scss$/ if you want to limit this loader
// to CKEditor 5's theme only.
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
minimize: true
}
},
'sass-loader'
]
}
]
},
output: {
path: bundle_output,
filename: 'openproject-[name].js',
library: '[name]'
},
resolve: {
modules: ['node_modules'],
extensions: ['.ts', '.tsx', '.js'],
alias: _.merge({
'core-components': path.resolve(__dirname, 'app', 'components'),
'op-ckeditor': path.resolve(__dirname, 'ckeditor'),
})
},
plugins: [
// Editor i18n TODO
new CKEditorWebpackPlugin({
// See https://ckeditor5.github.io/docs/nightly/ckeditor5/latest/features/ui-language.html
languages: [ 'en' ]
}),
// Clean the output directory
new CleanWebpackPlugin(['editor'], {
root: output_root,
verbose: true
})
]
};
return config;
}
module.exports = getWebpackCKEConfig;

@ -1,340 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
var webpack = require('webpack');
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var pathConfig = require('./rails-plugins.conf');
var autoprefixer = require('autoprefixer');
var dllManifest = require('./dist/vendors-dll-manifest.json')
var TypeScriptDiscruptorPlugin = require('./webpack/typescript-disruptor.plugin.js');
var ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
var HappyPack = require('happypack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var CleanWebpackPlugin = require('clean-webpack-plugin');
// var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// var CircularDependencyPlugin = require('circular-dependency-plugin');
var mode = (process.env['RAILS_ENV'] || 'production').toLowerCase();
var production = (mode !== 'development');
var debug_output = (!production || !!process.env['OP_FRONTEND_DEBUG_OUTPUT']);
var node_root = path.resolve(__dirname, 'node_modules');
var output_root = path.resolve(__dirname, '..', 'app', 'assets', 'javascripts');
var translations_root = path.resolve(__dirname, '..', 'config', 'locales', 'crowdin');
var bundle_output = path.resolve(output_root, 'bundles');
var pluginEntries = _.reduce(pathConfig.pluginNamesPaths, function (entries, pluginPath, name) {
entries[name.replace(/^openproject\-/, '')] = path.resolve(pluginPath, 'frontend', 'app', name + '-app.js');
return entries;
}, {});
var pluginAliases = _.reduce(pathConfig.pluginNamesPaths, function (entries, pluginPath, name) {
entries[name] = path.basename(pluginPath);
return entries;
}, {});
/** Extract available locales from openproject-translations plugin */
var localeIds = ['en'];
fs.readdirSync(translations_root).forEach(function (file) {
var matches = file.match( /^js-(.+)\.yml$/);
if (matches && matches.length > 1) {
localeIds.push(matches[1]);
}
});
var loaders = [
{
test: /\.tsx?$/,
include: [
path.resolve(__dirname, 'app'),
path.resolve(__dirname, 'tests')
].concat(_.values(pathConfig.pluginNamesPaths)),
use: [
{
loader: 'ng-annotate-loader'
},
{
loader: 'happypack/loader?id=ts'
}
]
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'postcss-loader'
],
publicPath: '/assets/bundles/'
})
},
{
test: /\.png$/,
use: [
{
loader: 'url-loader',
options: {
limit: '100000',
mimetype: 'image/png'
}
}
]
},
{
test: /\.gif$/,
use: ['file-loader']
},
{
test: /\.jpg$/,
use: ['file-loader']
},
{
test: /[\/].*\.js$/,
use: [
{
loader: 'ng-annotate-loader',
options: { map: true }
}
]
}
];
for (var k in pathConfig.pluginNamesPaths) {
if (pathConfig.pluginNamesPaths.hasOwnProperty(k)) {
loaders.push({
test: new RegExp('templates\/plugin-' + k.replace(/^openproject\-/, '') + '/.*\.html$'),
use: [
{
loader: 'ngtemplate-loader',
options: {
module: 'openproject.templates',
relativeTo: path.join(pathConfig.pluginNamesPaths[k], 'frontend', 'app')
}
},
{
loader: 'html-loader',
options: {
minimize: false
}
}
]
});
}
}
loaders.push({
test: /^((?!templates\/plugin).)*\.html$/,
use: [
{
loader: 'ngtemplate-loader',
options: {
module: 'openproject.templates',
relativeTo: path.resolve(__dirname, './app')
}
},
{
loader: 'html-loader',
options: {
minimize: false
}
}
]
});
function getWebpackMainConfig() {
config = {
context: path.resolve(__dirname, 'app'),
entry: _.merge({
'core-app': './openproject-app'
}, pluginEntries),
output: {
filename: 'openproject-[name].js',
path: bundle_output,
publicPath: '/assets/bundles/'
},
module: {
rules: loaders
},
resolve: {
modules: [
'node_modules',
],
extensions: ['.ts', '.tsx', '.js'],
// Allow empty import without extension
// enforceExtension: true,
alias: _.merge({
'locales': './../../config/locales',
'core-app': path.resolve(__dirname, 'app'),
'core-components': path.resolve(__dirname, 'app', 'components'),
'select2': path.resolve(__dirname, 'vendor', 'select2'),
'lodash': path.resolve(node_root, 'lodash', 'lodash.min.js'),
// prevents using crossvent from dist and by that
// reenables debugging in the browser console.
// https://github.com/bevacqua/dragula/issues/102#issuecomment-123296868
'crossvent': path.resolve(node_root, 'crossvent', 'src', 'crossvent.js')
}, pluginAliases)
},
externals: {
"I18n": "I18n"
},
plugins: [
// added to avoid module-duplication with plugins
new webpack.optimize.CommonsChunkPlugin({
name: "common",
filename: "openproject-common.js",
minChunks: 2
}),
// Add a simple fail plugin to return a status code of 2 if
// errors are detected (this includes TS warnings)
// It is ONLY executed when `ENV[CI]` is set or `--bail` is used.
TypeScriptDiscruptorPlugin,
// required for Angular (2+) to avoid error message:
// > WARNING in ../node_modules/@angular/core/@angular/core.es5.js
// > 5659:15-36 Critical dependency: the request of a dependency is an expression
new webpack.ContextReplacementPlugin(
/angular([\\\/])core/,
path.resolve(__dirname, '../src')
),
// Define modes for debug output
new webpack.DefinePlugin({
DEBUG: !!debug_output,
PRODUCTION: !!production
}),
new HappyPack({
id: 'ts',
threads: 4,
loaders: [
{
path: 'ts-loader',
query: {
happyPackMode: true,
logLevel: 'info',
configFile: path.resolve(__dirname, 'tsconfig.json')
}
}
]
}),
// Clean the output directory
new CleanWebpackPlugin(['bundles'], {
root: output_root,
verbose: true,
exclude: ['openproject-vendors.js']
}),
// Reference the vendors bundle
new webpack.DllReferencePlugin({
context: path.resolve(__dirname),
manifest: dllManifest
}),
// Parallel type checking for typescript
new ForkTsCheckerWebpackPlugin({
tsconfig: path.resolve(__dirname, 'tsconfig.json'),
// In happyPackMode, ts-loader no longer reports syntactic errors
checkSyntacticErrors: true
}),
// Extract CSS into its own bundle
new ExtractTextPlugin({
filename: 'openproject-[name].css',
disable: false
}),
// Global variables provided in all entries
// We should avoid this since it reduces webpack
// strengths to discover dependency use.
new webpack.ProvidePlugin({
'_': 'lodash'
}),
// Restrict loaded ngLocale locales to the ones we load from translations
new webpack.ContextReplacementPlugin(
/(angular-i18n)/,
new RegExp('angular-locale_(' + localeIds.join('|') + ')\.js$', 'i')
),
// Restrict loaded moment locales to the ones we load from translations
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, new RegExp('(' + localeIds.join('|') + ')\.js$', 'i')),
// Uncomment to analyze current bundle size
// new BundleAnalyzerPlugin()
// Uncomment to analyze potential cyclic dependencies between, e.g., Angular services.
// For simple imports in webpack, these are not a problem.
// new CircularDependencyPlugin({
// // exclude detection of files based on a RegExp
// exclude: /node_modules/,
// // add errors to webpack instead of warnings
// failOnError: false,
// // set the current working directory for displaying module paths
// cwd: process.cwd(),
// })
]
};
if (production) {
console.log("Applying webpack.optimize plugins for production.");
// Add compression and optimization plugins
// to the webpack build.
config.plugins.push(
new webpack.optimize.UglifyJsPlugin({
mangle: false,
compress: true,
compressor: { warnings: false },
sourceMap: false
}),
new webpack.LoaderOptionsPlugin({
// Let loaders know that we're in minification mode
minimize: true
})
);
}
return config;
}
module.exports = getWebpackMainConfig;

@ -1,45 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
var path = require('path');
var getWebpackMainConfig = require('./webpack-main-config');
function getWebpackTestConfig() {
var webpackConfig = getWebpackMainConfig();
webpackConfig.entry = './openproject-tests';
webpackConfig.output = {
path: path.join(__dirname, '/tests'),
filename: 'openproject-test-bundle.js'
};
return webpackConfig;
}
module.exports = getWebpackTestConfig;

@ -1,104 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
var webpack = require('webpack');
var path = require('path');
var CleanWebpackPlugin = require('clean-webpack-plugin');
var mode = (process.env['RAILS_ENV'] || 'production').toLowerCase();
var uglify = (mode !== 'development');
var output_root = path.resolve(__dirname, '..', 'app', 'assets', 'javascripts');
var bundle_output = path.resolve(output_root, 'bundles')
function getWebpackVendorsConfig() {
var config = {
entry: {
vendors: [path.resolve(__dirname, 'app', 'vendors.js')]
},
output: {
path: bundle_output,
filename: 'openproject-[name].js',
library: '[name]'
},
resolve: {
modules: ['node_modules'],
alias: {
'select2': path.resolve(__dirname, 'vendor', 'select2')
}
},
plugins: [
// required for Angular (2+) to avoid error message:
// > WARNING in ../node_modules/@angular/core/@angular/core.es5.js
// > 5659:15-36 Critical dependency: the request of a dependency is an expression
new webpack.ContextReplacementPlugin(
/angular([\\\/])core/,
path.resolve(__dirname, '../src')
),
new webpack.DllPlugin({
path: path.join(__dirname, "dist", "[name]-dll-manifest.json"),
name: "[name]",
context: '.'
}),
// Clean the output directory
new CleanWebpackPlugin(['bundles'], {
root: output_root,
verbose: true
})
]
};
if (uglify) {
console.log("Applying webpack.optimize plugins for production.");
// Add compression and optimization plugins
// to the webpack build.
config.plugins.push(
new webpack.optimize.UglifyJsPlugin({
mangle: true,
compress: true,
compressor: { warnings: false },
sourceMap: false,
exclude: /\.min\.js$/
}),
new webpack.LoaderOptionsPlugin({
// Let loaders know that we're in minification mode
minimize: true
})
);
}
return config;
}
module.exports = getWebpackVendorsConfig;

@ -1,43 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
var getWebpackMainConfig = require('./webpack-main-config');
var getWebpackTestConfig = require('./webpack-test-config');
module.exports = function(env) {
var configs = [
getWebpackMainConfig()
];
if (env && env.testconfig) {
console.log("Adding test config to build");
configs.push(getWebpackTestConfig());
}
return configs;
}

@ -1,33 +0,0 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
var mainConfig = require('./webpack-main-config');
module.exports = [
mainConfig()
];

@ -1,22 +0,0 @@
module.exports = function() {
this.plugin('done', function(stats) {
if (!(process.argv.indexOf('--bail') !== -1 || process.env.CI)) {
return;
}
if (process.env.TRAVIS_IGNORE_TYPESCRIPT) {
return;
}
var errors = stats.compilation.errors;
if (errors && errors.length) {
console.error(" ~~ The TYPESCRIPT DISCRUPTOR PLUGIN strikes again. ~~ ");
for (var i = 0, l = errors.length; i < l; i++) {
console.error(errors[i]);
}
process.exit(2);
}
});
};

@ -26,85 +26,14 @@
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'bundler'
require 'fileutils'
module ::OpenProject::Plugins
module FrontendLinking
##
# Register plugins with an Angular frontend to the CLI build.
# For that, search all gems with the group :opf_plugins
def self.register_from_bundle
openproject_frontend_plugins.tap do |plugins|
target_dir = Rails.root.join('frontend', 'src', 'app', 'modules', 'plugins', 'linked')
puts "Cleaning linked target directory #{target_dir}"
# Removing the current linked directory and recreate
FileUtils.remove_dir(target_dir, force: true)
FileUtils.mkdir_p(target_dir)
plugins.each do |name, path|
source = File.join(path, 'frontend', 'module')
target = File.join(target_dir, name)
puts "Linking frontend of OpenProject plugin #{name} to #{target}."
FileUtils.ln_sf(source, target)
end
generate_plugin_module(plugins)
end
end
##
# Regenerate the frontend plugin module orchestrating the linked frontends
def self.generate_plugin_module(plugins)
file_register = Rails.root.join('frontend', 'src', 'app', 'modules', 'plugins', 'linked-plugins.module.ts')
template_file = File.read(File.expand_path('../frontend_linking/linked-plugins.module.ts.erb', __FILE__))
template = ::ERB.new template_file,
nil,
'-'
puts "Regenerating frontend plugin registry #{file_register}."
context = ::OpenProject::Plugins::FrontendLinking::ErbContext.new plugins
result = template.result(context.get_binding)
File.open(file_register, 'w') { |file| file.write(result) }
end
def self.openproject_frontend_plugins
all_opf_plugin_paths.select do |name, path|
frontend_entry = File.join(path, 'frontend', 'module', 'main.ts')
File.readable? frontend_entry
end
end
##
# Print all gemspecs of registered OP plugins
# from the :opf_plugins group.
def self.known_opf_plugins
bundler_groups = %i[opf_plugins]
gemfile_path = Rails.root.join('Gemfile')
gems = Bundler::Dsl.evaluate(gemfile_path, '_temp_lockfile', true)
gems.dependencies
.each_with_object({}) do |dep, l|
l[dep.name] = dep if (bundler_groups & dep.groups).any?
end
.compact
end
##
# Get a mapping of all plugin specs and paths
def self.all_opf_plugin_paths
op_dep = known_opf_plugins
Bundler.load.specs.each_with_object({}) do |spec, h|
if op_dep.include?(spec.name)
options = spec.source.options
h[spec.name] = spec.full_gem_path
end
end
def self.regenerate!
Generator.new.regenerate!
end
end
end

@ -0,0 +1,155 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'bundler'
require 'fileutils'
module ::OpenProject::Plugins
module FrontendLinking
class Generator
attr_reader :openproject_plugins
def initialize
op_dep = load_known_opf_plugins
@openproject_plugins = Bundler.load.specs.each_with_object({}) do |spec, h|
if op_dep.include?(spec.name)
h[spec.name] = spec.full_gem_path
end
end
end
##
# Register plugins with an Angular frontend to the CLI build.
# For that, search all gems with the group :opf_plugins
def regenerate!
# Create links from plugins angular mdoules to frontend/src
regenerate_angular_links
# Create links from plugins legacy frontend to frontend/legacy/app/plugins
regenerate_legacy_links
end
private
##
# Register plugins with an Angular frontend to the CLI build.
# For that, search all gems with the group :opf_plugins
def regenerate_angular_links
all_angular_frontend_plugins.tap do |plugins|
target_dir = Rails.root.join('frontend', 'src', 'app', 'modules', 'plugins', 'linked')
puts "Cleaning linked target directory #{target_dir}"
# Removing the current linked directory and recreate
FileUtils.remove_dir(target_dir, force: true)
FileUtils.mkdir_p(target_dir)
plugins.each do |name, path|
source = File.join(path, 'frontend', 'module')
target = File.join(target_dir, name)
puts "Linking frontend of OpenProject plugin #{name} to #{target}."
FileUtils.ln_sf(source, target)
end
generate_plugin_module(plugins)
end
end
##
# Register plugins with a legacy frontend to the legacy build.
# For that, search all gems with the group :opf_plugins
def regenerate_legacy_links
all_legacy_frontend_plugins.tap do |plugins|
target_dir = Rails.root.join('frontend', 'legacy', 'app', 'plugins')
puts "Cleaning linked target directory #{target_dir}"
# Removing the current linked directory and recreate
FileUtils.remove_dir(target_dir, force: true)
FileUtils.mkdir_p(target_dir)
plugins.each do |name, path|
source = File.join(path, 'frontend', 'legacy-app')
target = File.join(target_dir, name)
puts "Linking legacy frontend of OpenProject plugin #{name} to #{target}."
FileUtils.ln_sf(source, target)
end
generate_plugin_module(plugins)
end
end
def all_angular_frontend_plugins
openproject_plugins.select do |_, path|
frontend_entry = File.join(path, 'frontend', 'module', 'main.ts')
File.readable? frontend_entry
end
end
def all_legacy_frontend_plugins
openproject_plugins.select do |_, path|
frontend_entry = File.join(path, 'frontend', 'legacy-app')
File.exists? frontend_entry
end
end
##
# Regenerate the frontend plugin module orchestrating the linked frontends
def generate_plugin_module(plugins)
file_register = Rails.root.join('frontend', 'src', 'app', 'modules', 'plugins', 'linked-plugins.module.ts')
template_file = File.read(File.expand_path('../linked-plugins.module.ts.erb', __FILE__))
template = ::ERB.new template_file,
nil,
'-'
puts "Regenerating frontend plugin registry #{file_register}."
context = ::OpenProject::Plugins::FrontendLinking::ErbContext.new plugins
result = template.result(context.get_binding)
File.open(file_register, 'w') { |file| file.write(result) }
end
##
# Print all gemspecs of registered OP plugins
# from the :opf_plugins group.
def load_known_opf_plugins
bundler_groups = %i[opf_plugins]
gemfile_path = Rails.root.join('Gemfile')
gems = Bundler::Dsl.evaluate(gemfile_path, '_temp_lockfile', true)
gems.dependencies
.each_with_object({}) do |dep, l|
l[dep.name] = dep if (bundler_groups & dep.groups).any?
end
.compact
end
end
end
end

@ -33,7 +33,7 @@ require 'open_project/assets'
# Otherwise Sprockets cannot find the files that webpack produces.
Rake::Task['assets:precompile']
.clear_prerequisites
.enhance(['assets:compile_environment', 'assets:prepare_op'])
.enhance(%w[openproject:plugins:register_frontend assets:compile_environment assets:prepare_op])
namespace :assets do
# In this task, set prerequisites for the assets:precompile task

@ -55,8 +55,8 @@ namespace :openproject do
end
desc 'Register plugins from the :opf_plugins bundle group to the frontend'
task :register_frontend do
::OpenProject::Plugins::FrontendLinking.register_from_bundle
task register_frontend: [:environment] do
::OpenProject::Plugins::FrontendLinking.regenerate!
end
end
end

Loading…
Cancel
Save