GUACAMOLE-773: Use Webpack rather than Webpack+Gulp.

This commit is contained in:
Michael Jumper
2021-04-06 10:42:02 -07:00
parent 1ef61687d8
commit 5f0ab7b3f3
15 changed files with 3599 additions and 2683 deletions

View File

@@ -1 +0,0 @@
org.webjars.bower:angular-module-shim:jar:0.0.4

View File

@@ -1,2 +1 @@
src/main/frontend/dist/**/* src/main/frontend/dist/**/*
src/main/frontend/generated/**/*

View File

@@ -1,4 +1,3 @@
*~ *~
node_modules node_modules
dist dist
generated

View File

@@ -1,34 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* @fileoverview External APIs referenced by the source of the Guacamole webapp
* and its dependencies.
* @externs
*/
// guacamole-common-js
const Guacamole = {};
// Web Storage API
const localStorage = {};
// matchMedia() function of Window object
const matchMedia = function matchMedia(str) {}

View File

@@ -1,105 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
const angularFilesort = require('gulp-angular-filesort');
const cleanCss = require('gulp-clean-css');
const concat = require('gulp-concat');
const del = require('del');
const gulp = require('gulp');
const ngHtml2Js = require("gulp-ng-html2js");
const sourcemaps = require('gulp-sourcemaps');
const webpack = require('webpack-stream');
// Clean build files
gulp.task('clean', (callback) => del([
'dist',
'generated'
], callback));
// Build monolithic, minified CSS source
gulp.task('build-css',
() => gulp.src([
'node_modules/@simonwep/pickr/dist/themes/monolith.min.css',
'src/app/**/*.css'
])
.pipe(sourcemaps.init())
.pipe(concat('guacamole.min.css'))
.pipe(cleanCss())
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('dist'))
);
// Pre-cache AngularJS templates
gulp.task('build-template-js',
() => gulp.src('src/app/**/*.html')
.pipe(ngHtml2Js({
moduleName: 'templates-main',
prefix: 'app/'
}))
.pipe(concat('templates.js'))
.pipe(gulp.dest('generated'))
);
// Build monolithic combined JavaScript source containing all pre-cached
// templates and all AngularJS module declarations in the proper order
gulp.task('build-combined-js',
() => gulp.src([
'src/app/**/*.js',
'generated/templates.js'
])
.pipe(angularFilesort())
.pipe(sourcemaps.init())
.pipe(concat('guacamole.js'))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('generated'))
);
// Process monolithic JavaScript source through WebPack to produce a bundle
// that contains all required dependencies
gulp.task('build-webpack-bundle',
() => gulp.src('generated/guacamole.js')
.pipe(webpack(require('./webpack.config.js')))
.pipe(gulp.dest('dist'))
);
// Build all JavaScript for the entire application
gulp.task('build-js', gulp.series(
'build-template-js',
'build-combined-js',
'build-webpack-bundle'
));
// Copy plain, static contents of application
gulp.task('copy-static',
() => gulp.src([
'src/relocateParameters.js',
'src/index.html',
'src/fonts/**/*',
'src/images/**/*',
'src/layouts/**/*',
'src/translations/**/*'
], { base: './src' })
.pipe(gulp.dest('dist'))
);
gulp.task('default', gulp.series(
'clean',
gulp.parallel('build-css', 'build-js', 'copy-static')
));

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,13 @@
{ {
"private": true, "private": true,
"scripts": { "scripts": {
"build": "gulp" "build": "webpack --progress"
}, },
"dependencies": { "dependencies": {
"@simonwep/pickr": "1.2.6", "@simonwep/pickr": "1.2.6",
"angular": "1.6.9", "angular": "1.6.9",
"angular-route": "1.6.9", "angular-route": "1.6.9",
"angular-templatecache-webpack-plugin": "^1.0.1",
"angular-touch": "1.6.9", "angular-touch": "1.6.9",
"angular-translate": "2.16.0", "angular-translate": "2.16.0",
"angular-translate-interpolation-messageformat": "2.16.0", "angular-translate-interpolation-messageformat": "2.16.0",
@@ -20,17 +21,16 @@
"messageformat": "1.0.2" "messageformat": "1.0.2"
}, },
"devDependencies": { "devDependencies": {
"clean-webpack-plugin": "^3.0.0",
"closure-webpack-plugin": "^2.5.0", "closure-webpack-plugin": "^2.5.0",
"del": "^6.0.0", "copy-webpack-plugin": "^5.1.2",
"css-loader": "^5.2.0",
"css-minimizer-webpack-plugin": "^1.3.0",
"exports-loader": "^1.1.1",
"google-closure-compiler": "^20210302.0.0", "google-closure-compiler": "^20210302.0.0",
"gulp": "^4.0.2", "html-webpack-plugin": "^4.5.2",
"gulp-angular-filesort": "^1.2.1", "mini-css-extract-plugin": "^1.4.0",
"gulp-clean-css": "^4.3.0",
"gulp-concat": "^2.6.1",
"gulp-ng-html2js": "^0.2.3",
"gulp-sourcemaps": "^3.0.0",
"source-map-loader": "^1.1.3",
"webpack": "^4.46.0", "webpack": "^4.46.0",
"webpack-stream": "^6.1.2" "webpack-cli": "^4.6.0"
} }
} }

View File

@@ -0,0 +1,56 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2014 Jed Richards
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
(function(angular) {
'use strict';
if ( !angular ) {
throw new Error('angular-module-shim: Missing Angular');
}
var origFn = angular.module;
var hash = {};
angular.module = function(name,requires,configFn) {
var requires = requires || [];
var registered = hash[name];
var module;
if ( registered ) {
module = origFn(name);
module.requires.push.apply(module.requires,requires);
// Register the config function if it exists.
if (configFn) {
module.config(configFn);
}
} else {
hash[name] = true;
module = origFn(name,requires,configFn);
}
return module;
};
})(window.angular);

View File

@@ -17,6 +17,8 @@
* under the License. * under the License.
*/ */
import '@simonwep/pickr/dist/themes/monolith.min.css'
/** /**
* A service for prompting the user to choose a color using the "Pickr" color * A service for prompting the user to choose a color using the "Pickr" color
* picker. As the Pickr color picker might not be available if the JavaScript * picker. As the Pickr color picker might not be available if the JavaScript

View File

@@ -17,10 +17,21 @@
* under the License. * under the License.
*/ */
require('angular-module-shim.js');
require('relocateParameters.js');
require('angular-translate-interpolation-messageformat');
require('angular-translate-loader-static-files');
/** /**
* The module for the root of the application. * The module for the root of the application.
*/ */
angular.module('index', [ angular.module('index', [
require('angular-route'),
require('angular-touch'),
require('angular-translate'),
'auth', 'auth',
'client', 'client',
'clipboard', 'clipboard',
@@ -28,11 +39,16 @@ angular.module('index', [
'login', 'login',
'manage', 'manage',
'navigation', 'navigation',
'ngRoute',
'ngTouch',
'notification', 'notification',
'pascalprecht.translate',
'rest', 'rest',
'settings', 'settings',
'templates-main' 'templates-main'
]); ]);
// Recursively pull in all other JavaScript and CSS files as requirements (just
// like old minify-maven-plugin build)
const context = require.context('../', true, /.*\.(css|js)$/);
context.keys().forEach(key => context(key));

View File

@@ -20,7 +20,7 @@
/** /**
* Provides the ClientIdentifier class definition. * Provides the ClientIdentifier class definition.
*/ */
angular.module('client').factory('ClientIdentifier', ['$injector', angular.module('navigation').factory('ClientIdentifier', ['$injector',
function defineClientIdentifier($injector) { function defineClientIdentifier($injector) {
// Required services // Required services

View File

@@ -27,8 +27,15 @@
<link rel="icon" type="image/png" href="images/logo-64.png"/> <link rel="icon" type="image/png" href="images/logo-64.png"/>
<link rel="icon" type="image/png" sizes="144x144" href="images/logo-144.png"/> <link rel="icon" type="image/png" sizes="144x144" href="images/logo-144.png"/>
<link rel="apple-touch-icon" type="image/png" href="images/logo-144.png"/> <link rel="apple-touch-icon" type="image/png" href="images/logo-144.png"/>
<link rel="stylesheet" type="text/css" href="guacamole.min.css?v=${project.version}">
<!-- Web application CSS (bundled by Webpack) -->
<% for (var index in htmlWebpackPlugin.files.css) { %>
<link rel="stylesheet" type="text/css" href="<%= htmlWebpackPlugin.files.css[index] %>"/>
<% } %>
<!-- Extension CSS (must be able to override webapp CSS) -->
<link rel="stylesheet" type="text/css" href="app.css?v=${project.version}"> <link rel="stylesheet" type="text/css" href="app.css?v=${project.version}">
<title ng-bind="page.title | translate"></title> <title ng-bind="page.title | translate"></title>
</head> </head>
<body ng-class="page.bodyClassName"> <body ng-class="page.bodyClassName">
@@ -68,11 +75,20 @@
</div> </div>
</div> </div>
<!-- Reformat URL for AngularJS if query parameters are present --> <!-- Core libraries that MUST be available at the global level (for use
<script type="text/javascript" src="relocateParameters.js"></script> by extensions) -->
<script type="text/javascript" src="guacamole-common-js/all.min.js"></script>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="lodash.min.js"></script>
<script type="text/javascript" src="angular.min.js"></script>
<!-- Web application --> <!-- Web application (bundled by Webpack) -->
<script type="text/javascript" src="guacamole.min.js?v=${project.version}"></script> <% for (var index in htmlWebpackPlugin.files.js) { %>
<script type="text/javascript" src="<%= htmlWebpackPlugin.files.js[index] %>"></script>
<% } %>
<script type="text/javascript" src="templates.js"></script>
<!-- Extension JavaScript -->
<script type="text/javascript" src="app.js?v=${project.version}"></script> <script type="text/javascript" src="app.js?v=${project.version}"></script>
</body> </body>

View File

@@ -17,54 +17,150 @@
* under the License. * under the License.
*/ */
const webpack = require('webpack'); const AngularTemplateCacheWebpackPlugin = require('angular-templatecache-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const ClosureWebpackPlugin = require('closure-webpack-plugin'); const ClosureWebpackPlugin = require('closure-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpack = require('webpack');
module.exports = { module.exports = {
bail: true, bail: true,
mode: 'production', mode: 'production',
stats: 'minimal',
output: { output: {
path: __dirname + '/dist', path: __dirname + '/dist',
filename: 'guacamole.min.js' filename: 'guacamole.[contenthash].js',
}, },
// Generate source maps, automatically building off any source maps // Generate source maps
// generated by Gulp
devtool: 'source-map', devtool: 'source-map',
// Entry point for the Guacamole webapp is the "index" AngularJS module
entry: './src/app/index/indexModule.js',
module: { module: {
rules: [ rules: [
// Automatically extract imported CSS for later reference within separate CSS file
{ {
test: /\.js$/, test: /\.css$/i,
enforce: 'pre', use: [
use: [ 'source-map-loader' ] MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
import: false,
url: false
}
} }
] ]
}, },
// Automatically require used modules /*
* Necessary to be able to use angular 1 with webpack as explained in https://github.com/webpack/webpack/issues/2049
*/
{
test: require.resolve('angular'),
loader: 'exports-loader',
options: {
type: 'commonjs',
exports: 'single window.angular'
}
}
]
},
optimization: {
minimizer: [
// Minify using Google Closure Compiler
new ClosureWebpackPlugin({ mode: 'STANDARD' }, {
languageIn: 'ECMASCRIPT6',
languageOut: 'ECMASCRIPT5',
compilationLevel: 'SIMPLE'
}),
new CssMinimizerPlugin()
],
splitChunks: {
cacheGroups: {
// Bundle CSS as one file
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true
}
}
}
},
plugins: [ plugins: [
new AngularTemplateCacheWebpackPlugin({
module: 'templates-main',
root: 'app/',
source: 'src/app/**/*.html',
standalone: true
}),
// Automatically clean out dist/ directory
new CleanWebpackPlugin(),
// Copy static files to dist/
new CopyPlugin([
{ from: 'fonts/**/*' },
{ from: 'images/**/*' },
{ from: 'translations/**/*' }
], {
context: 'src/'
}),
// Copy core libraries for global inclusion
new CopyPlugin([
{ from: 'angular/angular.min.js' },
{ from: 'jquery/dist/jquery.min.js' },
{ from: 'lodash/lodash.min.js' }
], {
context: 'node_modules/'
}),
// Generate index.html from template
new HtmlWebpackPlugin({
inject: false,
template: 'src/index.html'
}),
// Extract CSS from Webpack bundle as separate file
new MiniCssExtractPlugin({
filename: 'guacamole.[contenthash].css',
chunkFilename: '[id].guacamole.[contenthash].css'
}),
// Automatically require used modules
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
$: 'jquery',
_: 'lodash',
angular: 'angular',
jstz: 'jstz', jstz: 'jstz',
Pickr: '@simonwep/pickr', Pickr: '@simonwep/pickr',
saveAs: 'file-saver' saveAs: 'file-saver'
}) })
],
// Minify using Google Closure Compiler ],
optimization: { resolve: {
minimizer: [
new ClosureWebpackPlugin({ mode: 'STANDARD' }, { // Include Node modules and base source tree within search path for
externs: 'externs.js', // import/resolve
languageIn: 'ECMASCRIPT6', modules: [
languageOut: 'ECMASCRIPT5', 'src',
compilationLevel: 'ADVANCED' 'node_modules'
})
] ]
} }
}; };