feat(ui): lazy load whole API DevPortal page using $ocLazyLoad.
Some large deps are now only downloaded if you actually visit that page (e.g. markdown editor).
DELETE
manager/ui/war/gulpfile.js
-// Gulp File
-
-// ---------------------- Dependencies ---->>
-const browserify = require('browserify');
-const buffer = require('vinyl-buffer');
-const eventStream = require('event-stream');
-const fs = require('fs');
-const gulp = require('gulp');
-const map = require('vinyl-map');
-const path = require('path');
-const pkg = require('./package.json');
-const s = require('underscore.string');
-const source = require('vinyl-source-stream');
-const tsify = require("tsify");
-const glob = require("glob");
-
-// ---------------------- Gulp Plugins ---->>
-const angularTemplatecache = require('gulp-angular-templatecache');
-const clean = require('gulp-clean');
-const concat = require('gulp-concat');
-const concatCss = require('gulp-concat-css');
-const connect = require('gulp-connect');
-const notify = require('gulp-notify');
-const replace = require('gulp-replace');
-const runSequence = require('run-sequence');
-// const typescript = require('gulp-typescript');
-const watch = require('gulp-watch');
-
-module.exports = { gulp, connect }
-
-
-// ---------------------- Configuration ---->>
-
-var config = {
- debug: true,
- main: '.',
- ts: 'plugins/**/*.ts',
- templates: 'plugins/**/*.html',
- templateIncludes: 'plugins/**/*.include',
- templateModule: pkg.name + '-templates',
- dest: './dist/',
- js: pkg.name + '.js'
-};
-
-var SwaggerUIPath = './node_modules/swagger-ui-dist';
-
-
-
-// ---------------------- Individual Tasks ---->>
-
-
-// Default Task
-// Builds, watches for changes, and spins up the server
-gulp.task('default', function() {
- return runSequence('build', 'watch', 'connect');
-});
-
-
-
-// Browserify Task
-// Bundles node_modules required() in ./entry.js to be used on the client (/lib/scripts.js)
-gulp.task('browserify', function() {
- const allTsFiles = glob.sync('./plugins/api-manager/**/*.ts');
- const configFiles = ["./apiman/config.js", "./apiman/translations.js"];
- const allFiles = allTsFiles.concat(configFiles);
- return browserify({
- basedir: ".",
- debug: true,
- entries: ["./entry.ts"].concat(allFiles),
- cache: {},
- packageCache: {}
- })
- .plugin(tsify)
- .bundle()
- .on('error', function (error) { console.error(error.toString()); })
- .pipe(source('apiman-manager-uix.js')) // gives streaming vinyl file object
- .pipe(buffer()) // <----- convert from streaming to buffered vinyl file object
- .pipe(gulp.dest("dist"))
- //.pipe(process.stdout)
- // .pipe(gulp.dest(config.dest));
-});
-
-// gulp.task('', function() {
-// return browserify({
-// basedir: ".",
-// debug: true,
-// entries: ["./entry.ts"],
-// cache: {},
-// packageCache: {}
-// })
-// .plugin(tsify)
-// .bundle()
-// .on('error', function (error) { console.error(error.toString()); })
-// .pipe(source('deps.js')) // gives streaming vinyl file object
-// .pipe(buffer()) // <----- convert from streaming to buffered vinyl file object
-// .pipe(gulp.dest("dist"))
-// //.pipe(process.stdout)
-// // .pipe(gulp.dest(config.dest));
-// });
-
-
-// Build Task
-gulp.task('build', function() {
- return runSequence(['browserify', 'css', 'fonts', 'images'], 'path-adjust', 'clean-defs', 'template', 'concat', 'clean');
-});
-
-
-// Clean Task
-// Cleans the compiled.js and templates.js files
-// created in the 'tsc' and 'templates' tasks, respectively
-gulp.task('clean', function() {
- return gulp.src(['templates.js', 'compiled.js', 'deps.css'], {read: false})
- .pipe(clean());
-});
-
-
-// Clean-Defs Task
-// Cleans, or removes, definition file
-gulp.task('clean-defs', function() {
- return gulp.src('defs.d.ts', {read: false})
- .pipe(clean());
-});
-
-
-// Concat Task
-// Concatenates two files (compiled.js and templates.js) into ./dist/apiman-manager.js
-gulp.task('concat', function() {
- return gulp.src(['compiled.js', 'templates.js'])
- .pipe(concat(config.js))
- .pipe(gulp.dest(config.dest));
-});
-
-
-// Connect Task
-gulp.task('connect', function() {
- connect.server({
- root: '.',
- livereload: false,
- port: 2772,
- fallback: 'index.html'
- });
-});
-
-
-// CSS Task
-// Concatenates CSS files into one (/lib/styles.css)
-gulp.task('css', function() {
- return gulp.src(
- [
- 'node_modules/bootstrap-select/dist/css/bootstrap-select.css',
- 'node_modules/patternfly/dist/css/patternfly.css',
- 'node_modules/patternfly/dist/css/patternfly-additions.css',
- 'node_modules/ng-sortable/dist/ng-sortable.css',
- 'node_modules/c3/c3.css',
- 'node_modules/angular-xeditable-npm/dist/css/xeditable.css',
- 'node_modules/angular-scrollable-table/scrollable-table.css',
- 'node_modules/angular-ui-bootstrap/ui-bootstrap-csp.css',
- 'node_modules/ui-select/dist/select.css',
- 'node_modules/select2/select2.css',
- SwaggerUIPath + '/swagger-ui.css',
- 'node_modules/prismjs/themes/prism.css',
- 'node_modules/@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight.css',
- 'node_modules/@toast-ui/editor/dist/toastui-editor.css',
- 'node_modules/tui-color-picker/dist/tui-color-picker.css',
- 'node_modules/@toast-ui/editor-plugin-color-syntax/dist/toastui-editor-plugin-color-syntax.css'
- ], {base: 'node_modules/'})
- .pipe(concatCss('deps.css'))
- .pipe(gulp.dest('.'));
-});
-
-
-// Fonts Task
-// Copies all fonts to /lib/fonts
-gulp.task('fonts', function() {
- return gulp.src(
- [
- './node_modules/bootstrap/dist/fonts*.{eot,svg,ttf,woff,woff2}',
- './node_modules/font-awesome/fonts/*.{eot,svg,ttf,woff,woff2}',
- './node_modules/patternfly/dist/fonts/*.{eot,svg,ttf,woff,woff2}'
- ])
- .pipe(gulp.dest(config.dest + '/fonts'));
-});
-
-
-// Images Task
-// Copies all images to /lib/images
-gulp.task('images', function() {
- return gulp.src(
- [
- './node_modules/patternfly/dist/img/*.{png,jpg,gif}'
- ])
- .pipe(gulp.dest(config.dest + '/images'));
-});
-
-
-// Path-Adjust Task
-// Adjusts all paths within files, if necessary
-gulp.task('path-adjust', function() {
- // All CSS
- return gulp.src(['deps.css'])
- .pipe(replace('bootstrap/dist/fonts/', './fonts/'))
- .pipe(replace('font-awesome/fonts/', './fonts/'))
- .pipe(replace('patternfly/dist/fonts/', './fonts/'))
- .pipe(replace('patternfly/dist/img/', './images/'))
-
- .pipe(gulp.dest(config.dest));
-});
-
-
-
-// Reload Task
-gulp.task('reload', function() {
- gulp.src('.').pipe(connect.reload());
-});
-
-
-// Template Task
-// Creates the templates.js file in the project root (/manager/ui/war)
-gulp.task('template', function() {
- return gulp.src(config.templates)
- .pipe(angularTemplatecache({
- filename: 'templates.js',
- root: 'plugins/',
- standalone: true,
- module: config.templateModule
- }))
- .pipe(gulp.dest('.'));
-});
-
-// gulp.task('tsc', function() {
-// return browserify({
-// basedir: ".",
-// debug: true,
-// entries: ["plugins/api-manager/ts/apimanPlugin.ts"],
-// cache: {},
-// packageCache: {}
-// })
-// .plugin(tsify)
-// .bundle()
-// .pipe(source("apiman-manager-ui.js"))
-// .pipe(gulp.dest("dist"))
-// // return gulp.src(config.ts)
-// // .pipe(tsify)
-// // .pipe(concat('compiled.js'))
-// // .pipe(gulp.dest("dist"))
-// });
-// TSC Task
-// Compiles TS into JS
-// Creates the compiled.js file in the project root (/manager/ui/war)
-// gulp.task('tsc', function() {
-// var cwd = process.cwd();
-//
-// // Grab all TypeScript files (controllers, services, directives, etc.)
-// // Pipe them into the TypeScript project created above (config.tsProject)
-// var tsResult = gulp.src(config.ts)
-// .pipe(typescript(config.tsProject))
-// .on('error', notify.onError({
-// message: '#{ error.message }',
-// title: 'Typescript compilation error'
-// }));
-//
-// // eventStream.merge(argument1, argument2);
-// // argument1: Take the result of the TypeScript project (.js).
-// // Pipe it into, or concatenate it onto the compiled.js file.
-// // Then, pipe that file into the project root.
-// // argument2: Take the result of the TypeScript project (.dts).
-// // Pipe it into a d.ts. file.
-// // Pipe that file into the result of a buffer that gets passed to map();
-// return eventStream.merge(
-// tsResult.js
-// .pipe(concat('compiled.js'))
-// .pipe(gulp.dest('.')),
-// tsResult.dts
-// .pipe(gulp.dest('d.ts'))
-// ).pipe(map(function(buf, filename) {
-// if (!s.endsWith(filename, 'd.ts')) {
-// return buf;
-// }
-//
-// var relative = path.relative(cwd, filename);
-// fs.appendFileSync('defs.d.ts', '/// <reference path="' + relative + '"/>\n');
-// return buf;
-// }));
-// });
-
-
-// Watch Task
-// Builds, then watches for changes
-gulp.task('watch', function() {
- watch([
- 'entry.js',
- config.ts,
- config.templates,
- config.templateIncludes,
- 'plugins/api-manager/css/apiman.css',
- 'apiman/translations.js'
- ], function() {
- return runSequence(['browserify', 'css', 'fonts', 'images'], 'path-adjust', 'clean-defs', 'template', 'concat', 'clean');
- });
-});
-
-
\ No newline at end of file

MODIFY
manager/ui/war/package.json
manager/ui/war/package.json
"@types/luxon": "^2.0.5",
"@types/swagger-ui": "^3.52.0",
"@types/urijs": "^1.19.17",
+ "@types/webpack-env": "^1.16.3",
"copy-webpack-plugin": "^9.0.1",
"css-loader": "^6.4.0",
"cypress": "^7.3.0",
"html-webpack-plugin": "^5.3.2",
"http-proxy": "1.18.1",
"http-proxy-middleware": "^2.0.0",
+ "oclazyload": "^1.1.0",
"prettier": "^2.4.1",
"raw-loader": "^4.0.2",
"style-loader": "^3.3.0",

MODIFY
manager/ui/war/plugins/api-manager/ts/api/api-devportal.ts
manager/ui/war/plugins/api-manager/ts/api/api-devportal.ts
@@ -32,8 +32,7 @@ const toast = require("@toast-ui/editor");
const codeSyntaxHighlight = require("@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight-all");
const colourSyntax = require("@toast-ui/editor-plugin-color-syntax");
-
-_module.controller("Apiman.DevPortalController", [
+export const DevPortalController = _module.controller("Apiman.DevPortalController", [
"$q",
"$scope",
"$rootScope",

MODIFY
manager/ui/war/plugins/api-manager/ts/apimanPlugin.ts
manager/ui/war/plugins/api-manager/ts/apimanPlugin.ts
-// // Browserify imports
import * as $ from 'jquery'
// @ts-ignore
window.jQuery = $;
@@ -21,6 +20,7 @@ import 'angular-animate';
import 'angular-route';
import 'angular-schema-form';
import 'angular-scrollable-table/angular-scrollable-table.js';
+import 'oclazyload';
import 'ng-file-upload';
@@ -64,6 +64,7 @@ export const _module: IModule = angular.module(ApimanGlobals.pluginName, [
'ngFileUpload',
'ngAnimate',
'scrollable-table',
+ 'oc.lazyLoad',
// Apiman
'ApimanRPC',
'ApimanFilters',
@@ -78,15 +79,23 @@ export const _module: IModule = angular.module(ApimanGlobals.pluginName, [
_module.config([
'$locationProvider',
'$routeProvider',
- 'uiSelectConfig', ($locationProvider,
+ 'uiSelectConfig',
+ '$ocLazyLoadProvider',
+ function ($locationProvider,
$routeProvider,
- uiSelectConfig) => {
- var path = 'plugins/api-manager/html/';
- var prefix = '/api-manager';
+ uiSelectConfig,
+ $ocLazyLoadProvider) {
+
+ const path = 'plugins/api-manager/html/';
+ const prefix = '/api-manager';
uiSelectConfig.theme = 'select2';
uiSelectConfig.searchEnabled = false;
+ $ocLazyLoadProvider.config({
+ debug: true
+ });
+
// Define Routes
$routeProvider
@@ -190,7 +199,24 @@ _module.config([
templateUrl: path + 'api/api-plans.html'
})
.when(prefix + '/orgs/:org/apis/:api/:version/devportal', {
- templateUrl: path + 'api/api-devportal.html'
+ templateUrl: path + 'api/api-devportal.html',
+ // Resolves are completed before Angular tries to load the controller.
+ resolve: {
+ /**
+ * Lazy load the devportal to avoid dragging in dependencies many visitors won't use.
+ *
+ * For the settings we're stuck using, `require.ensure([], f())` which seems to be the only
+ * method that works with Webpack; the newer and recommended require-with-promise doesn't seem
+ * to function properly (no chunk is produced).
+ */
+ lazyLoad: ['$ocLazyLoad', function($ocLazyLoad) {
+ return require.ensure([], require => {
+ const entity = require(/* webpackChunkName: "devportal.chunk" */ './api/api-devportal.ts')
+ return $ocLazyLoad.load(entity.DevPortalController);
+ }
+ );
+ }]
+ }
})
.when(prefix + '/orgs/:org/apis/:api/:version/policies', {
templateUrl: path + 'api/api-policies.html'
@@ -482,7 +508,6 @@ import "./model/api.model.ts"
import "./model/contract.model.ts"
import "./apimanGlobals.ts"
import "./api/api-policies.ts"
-import "./api/api-devportal.ts"
import "./api/api-overview.ts"
import "./api/api-endpoint.ts"
import "./api/api.ts"

MODIFY
manager/ui/war/webpack.config.js
manager/ui/war/webpack.config.js
@@ -65,7 +65,7 @@ module.exports = (env, argv) => {
},
},
output: {
- filename: 'apiman-manager-ui-[name]-[contenthash].js',
+ filename: 'apiman-manager-ui-[name]-[chunkhash].js',
path: path.resolve(__dirname, 'dist'),
clean: true
}

MODIFY
manager/ui/war/yarn.lock
manager/ui/war/yarn.lock
resolved "https://registry.yarnpkg.com/@types/urijs/-/urijs-1.19.17.tgz#232ac9884b6a2aeab5dbe70b79cdb91d5067c325"
integrity sha512-ShIlp+8iNGo/yVVfYFoNRqUiaE9wMCzsSl85qTg2/C5l56BTJokU7QeMgVBQ9xhcyhWQP0zGXPBZPPvEG/sRmQ==
+"@types/webpack-env@^1.16.3":
+ version "1.16.3"
+ resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.16.3.tgz#b776327a73e561b71e7881d0cd6d34a1424db86a"
+ integrity sha512-9gtOPPkfyNoEqCQgx4qJKkuNm/x0R2hKR7fdl7zvTJyHnIisuE/LfvXOsYWL0o3qq6uiBnKZNNNzi3l0y/X+xw==
+
"@types/yauzl@^2.9.1":
version "2.9.2"
resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a"
@@ -3414,6 +3419,11 @@ obuf@^1.0.0, obuf@^1.1.2:
resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
+oclazyload@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/oclazyload/-/oclazyload-1.1.0.tgz#a9807322f190820a81c022f2ef1701d036d83e87"
+ integrity sha1-qYBzIvGQggqBwCLy7xcB0DbYPoc=
+
on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"

feat(ui): lazy load whole API DevPortal page using $ocLazyLoad.

Some large deps are now only downloaded if you actually visit that page (e.g. markdown editor).