ADD files for the new version
diff --git a/frontend/builders/builders.json b/frontend/builders/builders.json
new file mode 100644
index 0000000..8ec3ad4
--- /dev/null
+++ b/frontend/builders/builders.json
@@ -0,0 +1,9 @@
+{
+ "builders": {
+ "plugin": {
+ "class": "./plugin-builder",
+ "schema": "./plugin-builder/schema.json",
+ "description": "Plugin Builder"
+ }
+ }
+}
diff --git a/frontend/builders/package.json b/frontend/builders/package.json
new file mode 100644
index 0000000..f0b4a5b
--- /dev/null
+++ b/frontend/builders/package.json
@@ -0,0 +1,3 @@
+{
+ "builders": "builders.json"
+}
diff --git a/frontend/builders/plugin-builder/index.js b/frontend/builders/plugin-builder/index.js
new file mode 100644
index 0000000..3285a69
--- /dev/null
+++ b/frontend/builders/plugin-builder/index.js
@@ -0,0 +1,106 @@
+"use strict";
+/**
+ MIT License
+
+ Copyright (c) 2018 Alexey Zuev
+
+ Available from https://github.com/alexzuza/angular-plugin-architecture/
+
+ 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.
+ */
+Object.defineProperty(exports, "__esModule", { value: true });
+const build_angular_1 = require("@angular-devkit/build-angular");
+const fs = require("fs");
+const operators_1 = require("rxjs/operators");
+class PluginBuilder extends build_angular_1.BrowserBuilder {
+ patchEntryPoint(contents) {
+ fs.writeFileSync(this.entryPointPath, contents);
+ }
+ buildWebpackConfig(root, projectRoot, host, options) {
+ const { pluginName, sharedLibs } = this.options;
+ if (!this.options.modulePath) {
+ throw Error('Please define modulePath!');
+ }
+ if (!pluginName) {
+ throw Error('Please provide pluginName!');
+ }
+ const config = super.buildWebpackConfig(root, projectRoot, host, options);
+ // Make sure we are producing a single bundle
+ delete config.entry.polyfills;
+ delete config.optimization.runtimeChunk;
+ delete config.optimization.splitChunks;
+ delete config.entry.styles;
+ config.externals = {
+ rxjs: 'rxjs',
+ '@angular/common/http': 'ng.http',
+ '@angular/core': 'ng.core',
+ '@angular/common': 'ng.common',
+ '@angular/forms': 'ng.forms',
+ '@angular/router': 'ng.router',
+ tslib: 'tslib',
+ 'socket.io-client': 'socket.io-client',
+ };
+ if (sharedLibs) {
+ config.externals = [config.externals];
+ const sharedLibsArr = sharedLibs.split(',');
+ sharedLibsArr.forEach(sharedLibName => {
+ const factoryRegexp = new RegExp(`${sharedLibName}.ngfactory$`);
+ config.externals[0][sharedLibName] = sharedLibName; // define external for code
+ config.externals.push((context, request, callback) => {
+ if (factoryRegexp.test(request)) {
+ return callback(null, sharedLibName); // define external for factory
+ }
+ callback();
+ });
+ });
+ }
+ const ngCompilerPluginInstance = config.plugins.find(x => x.constructor && x.constructor.name === 'AngularCompilerPlugin');
+ if (ngCompilerPluginInstance) {
+ ngCompilerPluginInstance._entryModule = this.options.modulePath;
+ }
+ // preserve path to entry point
+ // so that we can clear use it within `run` method to clear that file
+ this.entryPointPath = config.entry.main[0];
+ const [modulePath, moduleName] = this.options.modulePath.split('#');
+ const factoryPath = `${modulePath.includes('.') ? modulePath : `${modulePath}/${modulePath}`}.ngfactory`;
+ const entryPointContents = `
+ export * from '${modulePath}';
+ export * from '${factoryPath}';
+ import { ${moduleName}NgFactory } from '${factoryPath}';
+ export default ${moduleName}NgFactory;
+ `;
+ this.patchEntryPoint(entryPointContents);
+ config.output.filename = `${pluginName}.js`;
+ config.output.library = pluginName;
+ config.output.libraryTarget = 'umd';
+ // workaround to support bundle on nodejs
+ config.output.globalObject = `(typeof self !== 'undefined' ? self : this)`;
+ return config;
+ }
+ run(builderConfig) {
+ this.options = builderConfig.options;
+ // I don't want to write it in my scripts every time so I keep it here
+ builderConfig.options.deleteOutputPath = false;
+ return super.run(builderConfig).pipe(operators_1.tap(() => {
+ // clear entry point so our main.ts is always empty
+ this.patchEntryPoint('');
+ }));
+ }
+}
+exports.default = PluginBuilder;
diff --git a/frontend/builders/plugin-builder/index.ts b/frontend/builders/plugin-builder/index.ts
new file mode 100644
index 0000000..1483821
--- /dev/null
+++ b/frontend/builders/plugin-builder/index.ts
@@ -0,0 +1,162 @@
+/**
+ MIT License
+
+ Copyright (c) 2018 Alexey Zuev
+
+ Available from https://github.com/alexzuza/angular-plugin-architecture/
+
+ 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.
+ */
+
+import {
+ BrowserBuilder,
+ NormalizedBrowserBuilderSchema
+} from '@angular-devkit/build-angular';
+import { Path, virtualFs } from '@angular-devkit/core';
+import * as fs from 'fs';
+import { Observable } from 'rxjs';
+
+import { BuilderConfiguration, BuildEvent } from '@angular-devkit/architect';
+import { tap } from 'rxjs/operators';
+
+interface PluginBuilderSchema extends NormalizedBrowserBuilderSchema {
+ /**
+ * A string of the form `path/to/file#exportName` that acts as a path to include to bundle
+ */
+ modulePath: string;
+
+ /**
+ * A name of compiled bundle
+ */
+ pluginName: string;
+
+ /**
+ * A comma-delimited list of shared lib names used by current plugin
+ */
+ sharedLibs: string;
+}
+export default class PluginBuilder extends BrowserBuilder {
+ private options: PluginBuilderSchema;
+
+ private entryPointPath: string;
+
+ patchEntryPoint(contents: string) {
+ fs.writeFileSync(this.entryPointPath, contents);
+ }
+
+ buildWebpackConfig(
+ root: Path,
+ projectRoot: Path,
+ host: virtualFs.Host<fs.Stats>,
+ options: PluginBuilderSchema
+ ) {
+ const { pluginName, sharedLibs } = this.options;
+
+ if (!this.options.modulePath) {
+ throw Error('Please define modulePath!');
+ }
+
+ if (!pluginName) {
+ throw Error('Please provide pluginName!');
+ }
+
+ const config = super.buildWebpackConfig(root, projectRoot, host, options);
+
+ // Make sure we are producing a single bundle
+ delete config.entry.polyfills;
+ delete config.optimization.runtimeChunk;
+ delete config.optimization.splitChunks;
+ delete config.entry.styles;
+
+ config.externals = {
+ rxjs: 'rxjs',
+ '@angular/common/http': 'ng.http',
+ '@angular/core': 'ng.core',
+ '@angular/common': 'ng.common',
+ '@angular/forms': 'ng.forms',
+ '@angular/router': 'ng.router',
+ tslib: 'tslib',
+ 'socket.io-client': 'socket.io-client',
+ // socketService: 'socketService'
+ // put here other common dependencies
+ };
+
+ if (sharedLibs) {
+ config.externals = [config.externals];
+ const sharedLibsArr = sharedLibs.split(',');
+ sharedLibsArr.forEach(sharedLibName => {
+ const factoryRegexp = new RegExp(`${sharedLibName}.ngfactory$`);
+ config.externals[0][sharedLibName] = sharedLibName; // define external for code
+ config.externals.push((context, request, callback) => {
+ if (factoryRegexp.test(request)) {
+ return callback(null, sharedLibName); // define external for factory
+ }
+ callback();
+ });
+ });
+ }
+
+ const ngCompilerPluginInstance = config.plugins.find(
+ x => x.constructor && x.constructor.name === 'AngularCompilerPlugin'
+ );
+ if (ngCompilerPluginInstance) {
+ ngCompilerPluginInstance._entryModule = this.options.modulePath;
+ }
+
+ // preserve path to entry point
+ // so that we can clear use it within `run` method to clear that file
+ this.entryPointPath = config.entry.main[0];
+
+ const [modulePath, moduleName] = this.options.modulePath.split('#');
+
+ const factoryPath = `${
+ modulePath.includes('.') ? modulePath : `${modulePath}/${modulePath}`
+ }.ngfactory`;
+ const entryPointContents = `
+ export * from '${modulePath}';
+ export * from '${factoryPath}';
+ import { ${moduleName}NgFactory } from '${factoryPath}';
+ export default ${moduleName}NgFactory;
+ `;
+ this.patchEntryPoint(entryPointContents);
+
+ config.output.filename = `${pluginName}.js`;
+ config.output.library = pluginName;
+ config.output.libraryTarget = 'umd';
+ // workaround to support bundle on nodejs
+ config.output.globalObject = `(typeof self !== 'undefined' ? self : this)`;
+
+ return config;
+ }
+
+ run(
+ builderConfig: BuilderConfiguration<PluginBuilderSchema>
+ ): Observable<BuildEvent> {
+ this.options = builderConfig.options;
+ // I don't want to write it in my scripts every time so I keep it here
+ builderConfig.options.deleteOutputPath = false;
+
+ return super.run(builderConfig).pipe(
+ tap(() => {
+ // clear entry point so our main.ts is always empty
+ this.patchEntryPoint('');
+ })
+ );
+ }
+}
diff --git a/frontend/builders/plugin-builder/schema.json b/frontend/builders/plugin-builder/schema.json
new file mode 100644
index 0000000..c9bdc72
--- /dev/null
+++ b/frontend/builders/plugin-builder/schema.json
@@ -0,0 +1,466 @@
+{
+ "$schema": "http://json-schema.org/schema",
+ "title": "Webpack browser schema for Build Facade.",
+ "description": "Browser target options",
+ "type": "object",
+ "properties": {
+ "modulePath": {
+ "type": "string",
+ "description": "Path to module like loadChildren",
+ "default": ""
+ },
+ "pluginName": {
+ "type": "string",
+ "description": "Name of bundled plugin",
+ "default": ""
+ },
+ "sharedLibs": {
+ "type": "string",
+ "description": "A comma-delimited list of shared lib names used by current plugin",
+ "default": ""
+ },
+ "assets": {
+ "type": "array",
+ "description": "List of static application assets.",
+ "default": [],
+ "items": {
+ "$ref": "#/definitions/assetPattern"
+ }
+ },
+ "main": {
+ "type": "string",
+ "description": "The full path for the main entry point to the app, relative to the current workspace.",
+ "$valueDescription": "fileName"
+ },
+ "polyfills": {
+ "type": "string",
+ "description": "The full path for the polyfills file, relative to the current workspace."
+ },
+ "tsConfig": {
+ "type": "string",
+ "description": "The full path for the TypeScript configuration file, relative to the current workspace."
+ },
+ "scripts": {
+ "description": "Global scripts to be included in the build.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "$ref": "#/definitions/extraEntryPoint"
+ }
+ },
+ "styles": {
+ "description": "Global styles to be included in the build.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "$ref": "#/definitions/extraEntryPoint"
+ }
+ },
+ "stylePreprocessorOptions": {
+ "description": "Options to pass to style preprocessors.",
+ "type": "object",
+ "properties": {
+ "includePaths": {
+ "description": "Paths to include. Paths will be resolved to project root.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ }
+ },
+ "additionalProperties": false
+ },
+ "optimization": {
+ "description": "Enables optimization of the build output.",
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "scripts": {
+ "type": "boolean",
+ "description": "Enables optimization of the scripts output.",
+ "default": true
+ },
+ "styles": {
+ "type": "boolean",
+ "description": "Enables optimization of the styles output.",
+ "default": true
+ }
+ },
+ "additionalProperties": false
+ },
+ {
+ "type": "boolean"
+ }
+ ]
+ },
+ "fileReplacements": {
+ "description": "Replace files with other files in the build.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/fileReplacement"
+ },
+ "default": []
+ },
+ "outputPath": {
+ "type": "string",
+ "description": "The full path for the new output directory, relative to the current workspace.\n\nBy default, writes output to a folder named dist/ in the current project."
+ },
+ "resourcesOutputPath": {
+ "type": "string",
+ "description": "The path where style resources will be placed, relative to outputPath.",
+ "default": ""
+ },
+ "aot": {
+ "type": "boolean",
+ "description": "Build using Ahead of Time compilation.",
+ "default": false
+ },
+ "sourceMap": {
+ "description": "Output sourcemaps.",
+ "default": true,
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "scripts": {
+ "type": "boolean",
+ "description": "Output sourcemaps for all scripts.",
+ "default": true
+ },
+ "styles": {
+ "type": "boolean",
+ "description": "Output sourcemaps for all styles.",
+ "default": true
+ },
+ "hidden": {
+ "type": "boolean",
+ "description": "Output sourcemaps used for error reporting tools.",
+ "default": false
+ },
+ "vendor": {
+ "type": "boolean",
+ "description": "Resolve vendor packages sourcemaps.",
+ "default": false
+ }
+ },
+ "additionalProperties": false
+ },
+ {
+ "type": "boolean"
+ }
+ ]
+ },
+ "vendorSourceMap": {
+ "type": "boolean",
+ "description": "Resolve vendor packages sourcemaps.",
+ "x-deprecated": true,
+ "default": false
+ },
+ "evalSourceMap": {
+ "type": "boolean",
+ "description": "Output in-file eval sourcemaps.",
+ "default": false,
+ "x-deprecated": true
+ },
+ "vendorChunk": {
+ "type": "boolean",
+ "description": "Use a separate bundle containing only vendor libraries.",
+ "default": true
+ },
+ "commonChunk": {
+ "type": "boolean",
+ "description": "Use a separate bundle containing code used across multiple bundles.",
+ "default": true
+ },
+ "baseHref": {
+ "type": "string",
+ "description": "Base url for the application being built."
+ },
+ "deployUrl": {
+ "type": "string",
+ "description": "URL where files will be deployed."
+ },
+ "verbose": {
+ "type": "boolean",
+ "description": "Adds more details to output logging.",
+ "default": false
+ },
+ "progress": {
+ "type": "boolean",
+ "description": "Log progress to the console while building."
+ },
+ "i18nFile": {
+ "type": "string",
+ "description": "Localization file to use for i18n."
+ },
+ "i18nFormat": {
+ "type": "string",
+ "description": "Format of the localization file specified with --i18n-file."
+ },
+ "i18nLocale": {
+ "type": "string",
+ "description": "Locale to use for i18n."
+ },
+ "i18nMissingTranslation": {
+ "type": "string",
+ "description": "How to handle missing translations for i18n."
+ },
+ "extractCss": {
+ "type": "boolean",
+ "description": "Extract css from global styles into css files instead of js ones.",
+ "default": false
+ },
+ "watch": {
+ "type": "boolean",
+ "description": "Run build when files change.",
+ "default": false
+ },
+ "outputHashing": {
+ "type": "string",
+ "description": "Define the output filename cache-busting hashing mode.",
+ "default": "none",
+ "enum": ["none", "all", "media", "bundles"]
+ },
+ "poll": {
+ "type": "number",
+ "description": "Enable and define the file watching poll time period in milliseconds."
+ },
+ "deleteOutputPath": {
+ "type": "boolean",
+ "description": "Delete the output path before building.",
+ "default": true
+ },
+ "preserveSymlinks": {
+ "type": "boolean",
+ "description": "Do not use the real path when resolving modules.",
+ "default": false
+ },
+ "extractLicenses": {
+ "type": "boolean",
+ "description": "Extract all licenses in a separate file.",
+ "default": false
+ },
+ "showCircularDependencies": {
+ "type": "boolean",
+ "description": "Show circular dependency warnings on builds.",
+ "default": true
+ },
+ "buildOptimizer": {
+ "type": "boolean",
+ "description": "Enables '@angular-devkit/build-optimizer' optimizations when using the 'aot' option.",
+ "default": false
+ },
+ "namedChunks": {
+ "type": "boolean",
+ "description": "Use file name for lazy loaded chunks.",
+ "default": true
+ },
+ "subresourceIntegrity": {
+ "type": "boolean",
+ "description": "Enables the use of subresource integrity validation.",
+ "default": false
+ },
+ "serviceWorker": {
+ "type": "boolean",
+ "description": "Generates a service worker config for production builds.",
+ "default": false
+ },
+ "ngswConfigPath": {
+ "type": "string",
+ "description": "Path to ngsw-config.json."
+ },
+ "skipAppShell": {
+ "type": "boolean",
+ "description": "Flag to prevent building an app shell.",
+ "default": false,
+ "x-deprecated": true
+ },
+ "index": {
+ "type": "string",
+ "description": "The name of the index HTML file."
+ },
+ "statsJson": {
+ "type": "boolean",
+ "description": "Generates a 'stats.json' file which can be analyzed using tools such as 'webpack-bundle-analyzer'.",
+ "default": false
+ },
+ "forkTypeChecker": {
+ "type": "boolean",
+ "description": "Run the TypeScript type checker in a forked process.",
+ "default": true
+ },
+ "lazyModules": {
+ "description": "List of additional NgModule files that will be lazy loaded. Lazy router modules will be discovered automatically.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "budgets": {
+ "description": "Budget thresholds to ensure parts of your application stay within boundaries which you set.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/budget"
+ },
+ "default": []
+ },
+ "profile": {
+ "type": "boolean",
+ "description": "Output profile events for Chrome profiler.",
+ "default": false
+ },
+ "es5BrowserSupport": {
+ "description": "Enables conditionally loaded ES2015 polyfills.",
+ "type": "boolean",
+ "default": false
+ },
+ "rebaseRootRelativeCssUrls": {
+ "description": "Change root relative URLs in stylesheets to include base HREF and deploy URL. Use only for compatibility and transition. The behavior of this option is non-standard and will be removed in the next major release.",
+ "type": "boolean",
+ "default": false,
+ "x-deprecated": true
+ }
+ },
+ "additionalProperties": false,
+ "required": ["outputPath", "index", "main", "tsConfig"],
+ "definitions": {
+ "assetPattern": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "glob": {
+ "type": "string",
+ "description": "The pattern to match."
+ },
+ "input": {
+ "type": "string",
+ "description": "The input directory path in which to apply 'glob'. Defaults to the project root."
+ },
+ "ignore": {
+ "description": "An array of globs to ignore.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "output": {
+ "type": "string",
+ "description": "Absolute path within the output."
+ }
+ },
+ "additionalProperties": false,
+ "required": ["glob", "input", "output"]
+ },
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "fileReplacement": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "src": {
+ "type": "string"
+ },
+ "replaceWith": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false,
+ "required": ["src", "replaceWith"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "replace": {
+ "type": "string"
+ },
+ "with": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false,
+ "required": ["replace", "with"]
+ }
+ ]
+ },
+ "extraEntryPoint": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "input": {
+ "type": "string",
+ "description": "The file to include."
+ },
+ "bundleName": {
+ "type": "string",
+ "description": "The bundle name for this extra entry point."
+ },
+ "lazy": {
+ "type": "boolean",
+ "description": "If the bundle will be lazy loaded.",
+ "default": false
+ }
+ },
+ "additionalProperties": false,
+ "required": ["input"]
+ },
+ {
+ "type": "string",
+ "description": "The file to include."
+ }
+ ]
+ },
+ "budget": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "The type of budget.",
+ "enum": ["all", "allScript", "any", "anyScript", "bundle", "initial"]
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the bundle."
+ },
+ "baseline": {
+ "type": "string",
+ "description": "The baseline size for comparison."
+ },
+ "maximumWarning": {
+ "type": "string",
+ "description": "The maximum threshold for warning relative to the baseline."
+ },
+ "maximumError": {
+ "type": "string",
+ "description": "The maximum threshold for error relative to the baseline."
+ },
+ "minimumWarning": {
+ "type": "string",
+ "description": "The minimum threshold for warning relative to the baseline."
+ },
+ "minimumError": {
+ "type": "string",
+ "description": "The minimum threshold for error relative to the baseline."
+ },
+ "warning": {
+ "type": "string",
+ "description": "The threshold for warning relative to the baseline (min & max)."
+ },
+ "error": {
+ "type": "string",
+ "description": "The threshold for error relative to the baseline (min & max)."
+ }
+ },
+ "additionalProperties": false,
+ "required": ["type"]
+ }
+ }
+}
diff --git a/frontend/builders/tsconfig.builders.json b/frontend/builders/tsconfig.builders.json
new file mode 100644
index 0000000..1088f22
--- /dev/null
+++ b/frontend/builders/tsconfig.builders.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "module": "commonjs",
+ "outDir": "./plugin-builder",
+ "importHelpers": false,
+ "sourceMap": false,
+ "target": "es6"
+ },
+ "files": [
+ "./plugin-builder/index.ts"
+ ]
+}