Commit ca77f94f authored by Mark Harding's avatar Mark Harding

(feat): working mvp of ssr

1 merge request!343WIP: Epic/ssr
Pipeline #108244248 failed with stages
in 3 minutes and 33 seconds
......@@ -3,7 +3,7 @@
"version": 1,
"newProjectRoot": "projects",
"projects": {
"v5.x": {
"minds": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
......@@ -12,7 +12,7 @@
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"index": "src/index.php",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"polyfills": "src/polyfills.ts",
......@@ -65,22 +65,22 @@
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "v5.x:build"
"browserTarget": "minds:build"
},
"configurations": {
"production": {
"browserTarget": "v5.x:build:production"
"browserTarget": "minds:build:production"
},
"hmr": {
"hmr": true,
"browserTarget": "v5.x:build:hmr"
"browserTarget": "minds:build:hmr"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "v5.x:build"
"browserTarget": "minds:build"
}
},
"test": {
......@@ -113,16 +113,18 @@
},
"configurations": {
"production": {
"fileReplacements": [{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}]
}
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
}
}
}
}
},
"v5.x-e2e": {
"minds-e2e": {
"root": "e2e",
"sourceRoot": "e2e",
"projectType": "application",
......@@ -131,7 +133,7 @@
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "./protractor.conf.js",
"devServerTarget": "v5.x:serve"
"devServerTarget": "minds:serve"
}
},
"lint": {
......@@ -144,7 +146,7 @@
}
}
},
"defaultProject": "v5.x",
"defaultProject": "minds",
"schematics": {
"@schematics/angular:component": {
"prefix": "m",
......
###
# Before running this container, ensure the server is built
###
FROM node:13-alpine
CMD node server
\ No newline at end of file
This diff is collapsed.
{
"name": "v5.x",
"name": "minds",
"version": "0.0.0",
"license": "MIT",
"scripts": {
......@@ -8,41 +8,49 @@
"preinstall": "git config core.hooksPath .git/hooks/",
"prebuild": "gulp build.sass",
"build": "ng build --prod",
"prebuild-dev": "gulp build.sass --deploy-url=http://localhost/en",
"build-dev": "ng build --output-path dist/en --deploy-url=/en/ --watch=true --poll=800",
"prebuild-dev": "gulp build.sass --deploy-url=http://localhost",
"build-dev": "ng build --output-path dist --deploy-url=/ --watch=true --poll=800",
"serve-dev": "ng serve --host=0.0.0.0 --deploy-url=/en/ --configuration=hmr --hmr --poll=800 --progress",
"test": "ng test",
"lint": "ng lint",
"e2e": "cypress run --debug",
"e2e-open": "cypress open",
"postinstall": "node patch.js"
"postinstall": "node patch.js",
"compile:server": "webpack --config webpack.server.config.js --progress --colors",
"serve:ssr": "node dist/server",
"build:ssr": "npm run build:client-and-server-bundles && npm run compile:server",
"build:client-and-server-bundles": "ng build --prod && ng run minds:server:production"
},
"private": true,
"dependencies": {
"@angular/animations": "~8.0.3",
"@angular/common": "~8.0.3",
"@angular/compiler": "~8.0.3",
"@angular/core": "~8.0.3",
"@angular/forms": "~8.0.3",
"@angular/animations": "~8.1.1",
"@angular/common": "~8.1.1",
"@angular/compiler": "~8.1.1",
"@angular/core": "~8.1.1",
"@angular/forms": "~8.1.1",
"@angular/http": "~7.2.0",
"@angular/platform-browser": "~8.0.3",
"@angular/platform-browser-dynamic": "~8.0.3",
"@angular/router": "~8.0.3",
"@nguniversal/common": "~8.0.3",
"@nguniversal/module-map-ngfactory-loader": "~8.0.3",
"@nguniversal/socket-engine": "~8.0.3",
"@angular/platform-browser": "~8.1.1",
"@angular/platform-browser-dynamic": "~8.1.1",
"@angular/platform-server": "~8.1.1",
"@angular/router": "~8.1.1",
"@gorniv/ngx-universal": "^2.0.1",
"@nguniversal/common": "~8.1.1",
"@nguniversal/express-engine": "^8.1.1",
"@nguniversal/module-map-ngfactory-loader": "~8.1.1",
"@nguniversal/socket-engine": "~8.1.1",
"@sentry/browser": "^5.6.2",
"angular-plotly.js": "^1.3.2",
"bn.js": "^4.11.8",
"braintree-web": "3.41.0",
"cookie-parser": "^1.4.4",
"core-js": "~2.6.2",
"dexie": "^2.0.4",
"ethjs": "~0.4.0",
"ethjs-account": "^0.1.4",
"ethjs-provider-signer": "^0.1.4",
"ethjs-signer": "0.1.1",
"express-http-proxy": "^1.6.0",
"global": "^4.3.2",
"indexeddbshim": "^4.1.0",
"material-datetime-picker": "git+https://github.com/Minds/material-datetime-picker.git",
"material-design-icons": "~3.0.1",
"material-design-lite": "~1.3.0",
......@@ -61,10 +69,10 @@
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.13.9",
"@angular/cli": "^7.2.1",
"@angular/compiler-cli": "~8.0.3",
"@angular/language-service": "~8.0.3",
"@angular-devkit/build-angular": "0.803.21",
"@angular/cli": "^8.1.1",
"@angular/compiler-cli": "~8.1.1",
"@angular/language-service": "~8.1.1",
"@angularclass/hmr": "^2.1.3",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.4",
......@@ -95,7 +103,7 @@
"ts-node": "~7.0.0",
"tslint": "~5.12.0",
"typescript": "~3.4.5",
"webpack-cli": "^3.3.2"
"webpack-cli": "^3.3.10"
},
"husky": {
"hooks": {
......
......@@ -4,25 +4,40 @@ import 'reflect-metadata';
import { join } from 'path';
import { readFileSync } from 'fs';
import { renderModuleFactory } from '@angular/platform-server';
import { ngExpressEngine } from '@nguniversal/express-engine';
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
import { enableProdMode } from '@angular/core';
import * as express from 'express';
import * as proxy from 'express-http-proxy';
import * as compression from 'compression';
import * as cookieparser from 'cookie-parser';
import { NgxRequest, NgxResponce } from '@gorniv/ngx-universal';
import * as xhr2 from 'xhr2';
const domino = require('domino');
const socketEngine = require('@nguniversal/socket-engine');
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
// activate cookie for server-side rendering
xhr2.prototype._restrictedHeaders.cookie = false;
xhr2.prototype._restrictedHeaders.cookie2 = false;
// xhr2.prototype._privateHeaders = {};
// Express server
const app = express();
const PORT = process.env.PORT || 4201;
const DIST_FOLDER = join(process.cwd(), 'dist/en');
const template = readFileSync(join(DIST_FOLDER, 'index.php')).toString();
// gzip
app.use(compression());
// cokies
app.use(cookieparser());
const PORT = process.env.PORT || 4200;
const DIST_FOLDER = join(process.cwd(), 'dist');
const template = readFileSync(join(DIST_FOLDER, 'index.html')).toString();
const win = domino.createWindow(template);
const setGlobalVars = require('indexeddbshim');
global['window'] = win;
global['Node'] = win.Node;
......@@ -32,22 +47,43 @@ global['Event']['prototype'] = win.Event.prototype;
global['document'] = win.document;
global['window']['Promise'] = global.Promise;
global['window']['Minds'] = {
cdn_urn: '/',
cdn_assets_url: '/en/',
cdn_url: '/',
cdn_assets_url: '/',
blockchain: {
network_address: "https://www.minds.com/api/v2/blockchain/proxy/",
network_address: 'https://www.minds.com/api/v2/blockchain/proxy/',
token: {
abi: [],
}
},
},
features: {
blockchain_creditcard: false,
'suggested-users': true,
helpdesk: true,
'top-feeds': true,
'top-feeds-filter': false,
'channel-filter-feeds': false,
'dark-mode': true,
'es-feeds': true,
'cassandra-notifications': true,
'media-modal': true,
'allow-comments-toggle': false,
permissions: false,
'wire-multi-currency': 'canary',
'cdn-jwt': false,
'post-scheduler': 'canary',
pro: false,
'purchase-pro': true,
'top-feeds-by-age': true,
},
};
global['XMLHttpRequest'] = xhr2; // Needed?
global['window']['localStorage'] = {
getItem: () => { },
setItem: () => { },
removeItem: () => { },
getItem: () => null,
setItem: () => {},
removeItem: () => {},
};
global['localStorage'] = global['window']['localStorage'];
global['window']['scrollTo'] = (pos) => { };
global['window']['scrollTo'] = pos => {};
Object.defineProperty(window.document, 'cookie', {
writable: true,
......@@ -64,30 +100,23 @@ Object.defineProperty(window.document, 'localStorage', {
value: global['window']['localStorage'],
});
setGlobalVars(null, {
checkOrigin: false
});
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('./dist/server/main');
const { provideModuleMap } = require('@nguniversal/module-map-ngfactory-loader');
socketEngine.startSocketEngine(AppServerModuleNgFactory, [ provideModuleMap(LAZY_MODULE_MAP) ]);
/*app.engine('html', (_, options, callback) => {
renderModuleFactory(AppServerModuleNgFactory, {
// Our index.html
document: template,
url: options.req.url,
// DI so that we can get lazy-loading to work differently (since we need it to just instantly render it)
extraProviders: [
provideModuleMap(LAZY_MODULE_MAP)
]
}).then(html => {
callback(null, html);
});
});
const {
AppServerModuleNgFactory,
LAZY_MODULE_MAP,
} = require('./dist/server/main');
const {
provideModuleMap,
} = require('@nguniversal/module-map-ngfactory-loader');
app.engine(
'html',
ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [provideModuleMap(LAZY_MODULE_MAP)],
})
);
app.set('view engine', 'html');
app.set('views', DIST_FOLDER);
......@@ -97,10 +126,59 @@ app.get('*.*', express.static(DIST_FOLDER));
// All regular routes use the Universal engine
app.get('*', (req, res) => {
res.render('index', { req });
const http =
req.headers['x-forwarded-proto'] === undefined
? 'http'
: req.headers['x-forwarded-proto'];
const url = req.originalUrl;
// tslint:disable-next-line:no-console
console.time(`GET: ${url}`);
res.render(
'index',
{
req: req,
res: res,
// provers from server
providers: [
// for http and cookies
{
provide: REQUEST,
useValue: req,
},
{
provide: RESPONSE,
useValue: res,
},
// for cookie
{
provide: NgxRequest,
useValue: req,
},
{
provide: NgxResponce,
useValue: res,
},
// for absolute path
{
provide: 'ORIGIN_URL',
useValue: `${http}://${req.headers.host}`,
},
],
},
(err, html) => {
if (!!err) {
throw err;
}
// tslint:disable-next-line:no-console
console.timeEnd(`GET: ${url}`);
res.send(html);
}
);
});
// Start up the Node server
app.listen(PORT, () => {
console.log(`Node server listening on http://localhost:${PORT}`);
});*/
});
......@@ -3,10 +3,14 @@ import { NgModule } from '@angular/core';
import { MindsModule } from './app.module';
import { Minds } from './app.component';
import * as PlotlyJS from 'plotly.js/dist/plotly.js';
import { PlotlyModule } from 'angular-plotly.js';
PlotlyModule.plotlyjs = PlotlyJS;
@NgModule({
imports: [
MindsModule,
],
imports: [MindsModule, PlotlyModule],
bootstrap: [Minds],
providers: [{ provide: 'ORIGIN_URL', useValue: location.origin }],
})
export class AppBrowserModule {}
......@@ -29,6 +29,7 @@ import { SsoService } from './common/services/sso.service';
import { Subscription } from 'rxjs';
import { RouterHistoryService } from './common/services/router-history.service';
import { PRO_DOMAIN_ROUTES } from './modules/pro/pro.module';
import { ConfigsService } from './common/services/configs.service';
@Component({
moduleId: module.id,
......@@ -71,6 +72,7 @@ export class Minds {
private routerHistoryService: RouterHistoryService,
private site: SiteService,
private sso: SsoService,
private configs: ConfigsService,
private cd: ChangeDetectorRef
) {
this.name = 'Minds';
......@@ -82,7 +84,11 @@ export class Minds {
async ngOnInit() {
try {
this.diagnostics.setUser(this.minds.user);
// Load external configs
await this.configs.loadFromRemote();
// Setup sentry/diagnostic configs
this.diagnostics.setUser(this.configs.get('user'));
this.diagnostics.listen(); // Listen for user changes
if (this.sso.isRequired()) {
......@@ -115,12 +121,11 @@ export class Minds {
this.showOnboarding = await this.onboardingService.showModal();
}
if (this.minds.user.language !== this.minds.language) {
console.log(
'[app]:: language change',
this.minds.user.language,
this.minds.language
);
const user = this.session.getLoggedInUser();
const language = this.configs.get('language');
if (user.language !== language) {
console.log('[app]:: language change', user.language, language);
window.location.reload(true);
}
}
......
......@@ -8,6 +8,7 @@ import {
BrowserModule,
BrowserTransferStateModule,
} from '@angular/platform-browser';
// import { TransferHttpCacheModule } from '@nguniversal/common';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
......@@ -80,7 +81,8 @@ import * as Sentry from '@sentry/browser';
Sentry.init({
dsn: 'https://3f786f8407e042db9053434a3ab527a2@sentry.io/1538008', // TODO: do not hardcard
release: environment.version,
environment: (<any>window.Minds).environment || 'development',
// environment: (<any>window.Minds).environment || 'development',
environment: 'srr-test', // TODO: fix
});
@Injectable()
......@@ -104,6 +106,7 @@ export class SentryErrorHandler implements ErrorHandler {
imports: [
BrowserModule.withServerTransition({ appId: 'm-app' }),
BrowserTransferStateModule,
// TransferHttpCacheModule,
//BrowserAnimationsModule,
ReactiveFormsModule,
FormsModule,
......@@ -156,6 +159,7 @@ export class SentryErrorHandler implements ErrorHandler {
CanaryModule,
ChannelsModule,
UpgradesModule,
//PlotlyModule,
//last due to :username route
ChannelContainerModule,
......
......@@ -5,6 +5,7 @@ import { ServerTransferStateModule } from '@angular/platform-server';
import { MindsModule } from './app.module';
import { Minds } from './app.component';
import { PlotlyModule } from 'angular-plotly.js';
@NgModule({
imports: [
......@@ -12,6 +13,7 @@ import { Minds } from './app.component';
ServerModule,
ModuleMapLoaderModule,
ServerTransferStateModule,
PlotlyModule,
],
bootstrap: [Minds],
})
......
import { NgModule } from '@angular/core';
import { CommonModule as NgCommonModule } from '@angular/common';
import { NgModule, inject } from '@angular/core';
import {
CommonModule as NgCommonModule,
isPlatformServer,
} from '@angular/common';
import { RouterModule, Router, Routes } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
......@@ -116,7 +119,7 @@ import { ToggleComponent } from './components/toggle/toggle.component';
import { MarketingAsFeaturedInComponent } from './components/marketing/as-featured-in.component';
import { SidebarMenuComponent } from './components/sidebar-menu/sidebar-menu.component';
import { ChartV2Component } from './components/chart-v2/chart-v2.component';
import * as PlotlyJS from 'plotly.js/dist/plotly.js';
//import * as PlotlyJS from 'plotly.js/dist/plotly.js';
import { PlotlyModule } from 'angular-plotly.js';
import { PageLayoutComponent } from './components/page-layout/page-layout.component';
import { DashboardLayoutComponent } from './components/dashboard-layout/dashboard-layout.component';
......@@ -128,8 +131,7 @@ import { FormDescriptorComponent } from './components/form-descriptor/form-descr
import { FormToastComponent } from './components/form-toast/form-toast.component';
import { SsoService } from './services/sso.service';
import { EmailConfirmationComponent } from './components/email-confirmation/email-confirmation.component';
PlotlyModule.plotlyjs = PlotlyJS;
import { ConfigsService } from './services/configs.service';
const routes: Routes = [
{
......@@ -420,6 +422,11 @@ const routes: Routes = [
useFactory: router => new RouterHistoryService(router),
deps: [Router],
},
{
provide: ConfigsService,
useFactory: client => new ConfigsService(client),
deps: [Client],
},
],
entryComponents: [
NotificationsToasterComponent,
......
......@@ -4,10 +4,13 @@ import {
Component,
OnDestroy,
OnInit,
Inject,
PLATFORM_ID,
} from '@angular/core';
import { EmailConfirmationService } from './email-confirmation.service';
import { Session } from '../../../services/session';
import { Subscription } from 'rxjs';
import { isPlatformBrowser } from '@angular/common';
/**
* Component that displays an announcement-like banner
......@@ -33,7 +36,8 @@ export class EmailConfirmationComponent implements OnInit, OnDestroy {
constructor(
protected service: EmailConfirmationService,
protected session: Session,
protected cd: ChangeDetectorRef
protected cd: ChangeDetectorRef,
@Inject(PLATFORM_ID) protected platformId
) {}
ngOnInit(): void {
......@@ -46,10 +50,12 @@ export class EmailConfirmationComponent implements OnInit, OnDestroy {
this.detectChanges();
});
this.canCloseTimer = window.setTimeout(() => {
this.canClose = true;
this.detectChanges();
}, 3000);
if (isPlatformBrowser(this.platformId)) {
this.canCloseTimer = window.setTimeout(() => {
this.canClose = true;
this.detectChanges();
}, 3000);
}
}
ngOnDestroy(): void {
......
......@@ -72,10 +72,7 @@ export class MindsRichEmbed {
// Inline Embedding
let inlineEmbed = this.parseInlineEmbed(this.inlineEmbed);
if (
this.featureService.has('media-modal') &&
this.mediaSource === 'youtube'
) {
if (this.mediaSource === 'youtube') {
this.modalRequestSubscribed =
this.mediaModalRequested.observers.length > 0;
}
......@@ -96,11 +93,7 @@ export class MindsRichEmbed {
this.inlineEmbed = inlineEmbed;
if (
this.modalRequestSubscribed &&
this.featureService.has('media-modal') &&
this.mediaSource === 'youtube'
) {
if (this.modalRequestSubscribed && this.mediaSource === 'youtube') {
if (this.inlineEmbed && this.inlineEmbed.htmlProvisioner) {
this.inlineEmbed.htmlProvisioner().then(html => {
this.inlineEmbed.html = html;
......@@ -270,11 +263,9 @@ export class MindsRichEmbed {
}
hasInlineContentLoaded() {
return this.featureService.has('media-modal')
? !this.modalRequestSubscribed &&
this.inlineEmbed &&
this.inlineEmbed.html
: this.embeddedInline && this.inlineEmbed && this.inlineEmbed.html;
return (
!this.modalRequestSubscribed && this.inlineEmbed && this.inlineEmbed.html
);
}
detectChanges() {
......
......@@ -14,6 +14,7 @@ export class BlockListService {
protected storage: Storage
) {
this.blocked = new BehaviorSubject(JSON.parse(this.storage.get('blocked')));
//this.blocked = new BehaviorSubject(null);
}
fetch() {
......@@ -24,7 +25,8 @@ export class BlockListService {
this.blocked.next(response.guids); // re-emit as we have a change
this.storage.set('blocked', JSON.stringify(response.guids)); // save to storage
});
})
.catch(err => null);
return this;
}
......
import { Client } from '../api/client.service';
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
@Injectable()
export class ConfigsService {
private configs = {};
constructor(private client: Client) {}
async loadFromRemote() {
try {
this.configs = await this.client.get('api/v1/minds/config');
console.log(this.configs);
} catch (err) {
console.error(err);
}
}
get(key) {
return this.configs[key] || {};
}
}
......@@ -4,17 +4,18 @@
* @returns {boolean}
*/
export default function isMobile() {
let check = false;
(function(a) {
if (
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(
a
) ||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
a.substr(0, 4)
)
)
check = true;
})(navigator.userAgent || navigator.vendor || (<any>window).opera);
return check;
// let check = false;
// (function(a) {
// if (
// /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(
// a
// ) ||
// /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
// a.substr(0, 4)
// )
// )
// check = true;
// })(navigator.userAgent || navigator.vendor || (<any>window).opera);
// return check;
return false;
}
import { NgModule } from '@angular/core';
import * as PlotlyJS from 'plotly.js/dist/plotly.js';
//import * as PlotlyJS from 'plotly.js/dist/plotly.js';
import { PlotlyModule } from 'angular-plotly.js';
import { AdminAnalyticsComponent } from './pages/admin/admin.component';
......@@ -63,7 +63,7 @@ import { FormsModule } from '@angular/forms';
import { AnalyticsSearchSuggestionsComponent } from './v2/components/search-suggestions/search-suggestions.component';
import { AnalyticsBenchmarkComponent } from './v2/components/benchmark/benchmark.component';
PlotlyModule.plotlyjs = PlotlyJS;
//PlotlyModule.plotlyjs = PlotlyJS;
const routes: Routes = [
{
......
......@@ -91,10 +91,10 @@ export class NotificationService {
*/
getNotifications() {
const pollIntervalSeconds = 60;
this.notificationPollTimer = timer(0, pollIntervalSeconds * 1000);
this.updateNotificationCountSubscription = this.notificationPollTimer.subscribe(
() => this.updateNotificationCount()
);
// this.notificationPollTimer = timer(0, pollIntervalSeconds * 1000);
// this.updateNotificationCountSubscription = this.notificationPollTimer.subscribe(
// () => this.updateNotificationCount()
// );
}
updateNotificationCount() {
......@@ -125,7 +125,7 @@ export class NotificationService {
this.title.setCounter(window.Minds.notifications_count);
}
ngOnDestroy() {
this.updateNotificationCountSubscription.unsubscribe();
}
// ngOnDestroy() {
// this.updateNotificationCountSubscription.unsubscribe();
// }
}
......@@ -5,29 +5,34 @@ import { HttpClient, HttpHeaders } from '@angular/common/http';
import { makeStateKey, TransferState } from '@angular/platform-browser';
import { environment } from '../../../environments/environment';
import { Location } from '@angular/common';
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
/**
* API Class
*/
export class Client {
base: string = 'https://eggman.minds.com/';
base: string = '/';
cookie: Cookie = new Cookie();
static _(
http: HttpClient,
platformId,
location: Location,
transferState: TransferState
platformId,
transferState: TransferState,
@Inject('ORIGIN_URL') baseUrl: string
) {
return new Client(http, platformId, location, transferState);
return new Client(http, location, platformId, transferState, baseUrl);
}
constructor(
public http: HttpClient,
@Inject(PLATFORM_ID) private platformId,
public location: Location,
private transferState: TransferState
) {}
@Inject(PLATFORM_ID) private platformId,
private transferState: TransferState,
@Inject('ORIGIN_URL') public baseUrl: string
) {
this.base = `${baseUrl}/`;
}
/**
* Return a GET request
......@@ -37,16 +42,6 @@ export class Client {
endpoint += '?' + this.buildParams(data);
}
const STATE_KEY = makeStateKey(
`http-${endpoint}` + JSON.stringify(options)
);
if (this.transferState.hasKey(STATE_KEY)) {
const result = this.transferState.get(STATE_KEY, null);
this.transferState.remove(STATE_KEY);
return Promise.resolve(JSON.parse(result));
}
return new Promise((resolve, reject) => {
this.http.get(this.base + endpoint, this.buildOptions(options)).subscribe(
res => {
......@@ -262,7 +257,9 @@ export class Client {
*/
private buildOptions(options: Object, withCredentials: boolean = false) {
if (isPlatformServer(this.platformId)) {
return options; // TODO: support XSRF on universal
return {
withCredentials: true,
};
}
const XSRF_TOKEN = this.cookie.get('XSRF-TOKEN') || '';
......
import { Injectable } from '@angular/core';
import { PLATFORM_ID, Inject, Injector } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import {
HttpEvent,
HttpInterceptor,
HttpHandler,
HttpRequest,
} from '@angular/common/http';
import { Observable } from 'rxjs';
import * as xhr2 from 'xhr2';
import * as express from 'express';
import { REQUEST } from '@nguniversal/express-engine/tokens';
@Injectable()
export class CookieHttpInterceptorService implements HttpInterceptor {
constructor(
@Inject(PLATFORM_ID) private platformId: string,
private injector: Injector
) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
request = request.clone({ withCredentials: true });
if (!isPlatformBrowser(this.platformId)) {
// Be careful! This should only be done on the server side!!
// hrow xhr2.prototype;
// xhr2.prototype._restrictedHeaders = {};
let req: express.Request = this.injector.get(REQUEST);
let rootDomain = req.hostname
.split('.')
.slice(-2)
.join('.');
if (request.url.match(/^https?:\/\/([^/:]+)/)[1].endsWith(rootDomain)) {
let cookieString = Object.keys(req.cookies).reduce(
(accumulator, cookieName) => {
accumulator += cookieName + '=' + req.cookies[cookieName] + ';';
return accumulator;
},
''
);
console.log(cookieString);
// throw xhr2.prototype;
request = request.clone({
headers: request.headers.set('Cookie', cookieString),
});
}
}
return next.handle(request);
}
}
import { Injectable, isDevMode } from '@angular/core';
import { Session } from './session';
import { Router } from '@angular/router';
import { ConfigsService } from '../common/services/configs.service';
import { Subscription, Observable } from 'rxjs';
import { first } from 'rxjs/operators';
@Injectable()
export class FeaturesService {
protected _features: any;
protected _warnedCache: { [key: string]: number } = {};
private features = {};
constructor(private session: Session, private router: Router) {
this._features = window.Minds.features || {};
}
constructor(
private session: Session,
private router: Router,
private configs: ConfigsService
) {}
has(feature: string): boolean {
const features = this.configs.get('features');
has(feature: string) {
if (!feature) {
throw new Error('Invalid feature ID');
}
......@@ -20,7 +27,7 @@ export class FeaturesService {
return !this.has(feature.substring(1));
}
if (typeof this._features[feature] === 'undefined') {
if (typeof features[feature] === 'undefined') {
if (isDevMode() && !this._hasWarned(feature)) {
this._warnedCache[feature] = Date.now();
}
......@@ -28,21 +35,21 @@ export class FeaturesService {
return false;
}
if (this._features[feature] === 'admin' && this.session.isAdmin()) {
if (features[feature] === 'admin' && this.session.isAdmin()) {
return true;
}
if (
this._features[feature] === 'canary' &&
features[feature] === 'canary' &&
this.session.getLoggedInUser().canary
) {
return true;
}
return this._features[feature] === true;
return features[feature] === true;
}
check(feature: string, { redirectTo }: { redirectTo?: any[] } = {}) {
async check(feature: string, { redirectTo }: { redirectTo?: any[] } = {}) {
if (feature.indexOf('!') === 0) {
throw new Error('Cannot negate feature when using check()');
}
......@@ -65,7 +72,7 @@ export class FeaturesService {
return this._warnedCache[feature] + 5000 < Date.now();
}
static _(session: Session, router: Router) {
return new FeaturesService(session, router);
static _(session: Session, router: Router, configs: ConfigsService) {
return new FeaturesService(session, router, configs);
}
}
......@@ -41,7 +41,9 @@ export class LoginReferrerService {
}
unlisten(): this {
this._routerListener.unsubscribe();
if (this._routerListener) {
this._routerListener.unsubscribe();
}
return this;
}
......
......@@ -10,6 +10,7 @@ export class Navigation {
getItems(container: string = 'sidebar'): Array<any> {
var navigation: Array<any> = window.Minds.navigation;
if (!navigation) return [];
var items: Array<any> = navigation[container];
if (!items) return [];
......
......@@ -35,7 +35,7 @@ import { BlockchainService } from '../modules/blockchain/blockchain.service';
import { WebtorrentService } from '../modules/webtorrent/webtorrent.service';
import { TimeDiffService } from './timediff.service';
import { UpdateMarkersService } from '../common/services/update-markers.service';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http';
import { BlockListService } from '../common/services/block-list.service';
import { EntitiesService } from '../common/services/entities.service';
import { InMemoryStorageService } from './in-memory-storage.service';
......@@ -47,6 +47,9 @@ import { SiteService } from '../common/services/site.service';
import { SessionsStorageService } from './session-storage.service';
import { DiagnosticsService } from './diagnostics.service';
import { FormToastService } from '../common/services/form-toast.service';
import { ConfigsService } from '../common/services/configs.service';
import { TransferHttpInterceptorService } from './transfer-http-interceptor.service';
import { CookieHttpInterceptorService } from './api/cookie-http-interceptor.service';
export const MINDS_PROVIDERS: any[] = [
SiteService,
......@@ -68,13 +71,23 @@ export const MINDS_PROVIDERS: any[] = [
{
provide: Client,
useFactory: Client._,
deps: [HttpClient, Location],
deps: [HttpClient, Location, PLATFORM_ID, TransferState, 'ORIGIN_URL'],
},
{
provide: Upload,
useFactory: Upload._,
deps: [HttpClient],
},
{
provide: HTTP_INTERCEPTORS,
useClass: TransferHttpInterceptorService,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: CookieHttpInterceptorService,
multi: true,
},
{
provide: Storage,
useFactory: Storage._,
......@@ -110,11 +123,7 @@ export const MINDS_PROVIDERS: any[] = [
useFactory: RichEmbedService._,
deps: [Client],
},
{
provide: Session,
useFactory: Session._,
deps: [SiteService],
},
Session,
{
provide: ThirdPartyNetworksService,
useFactory: ThirdPartyNetworksService._,
......@@ -191,10 +200,15 @@ export const MINDS_PROVIDERS: any[] = [
useFactory: ContextService._,
deps: [Router, Storage, Client],
},
{
provide: ConfigsService,
useFactory: client => new ConfigsService(client),
deps: [Client],
},
{
provide: FeaturesService,
useFactory: FeaturesService._,
deps: [Session, Router],
deps: [Session, Router, ConfigsService],
},
{
provide: BlockchainService,
......
......@@ -22,7 +22,7 @@ export class ScrollToTopService {
}
unlisten(): this {
this._routerListener.unsubscribe();
if (this._routerListener) this._routerListener.unsubscribe();
return this;
}
}
......@@ -2,14 +2,13 @@
* Sessions
*/
import { EventEmitter } from '@angular/core';
import { ConfigsService } from '../common/services/configs.service';
export class Session {
loggedinEmitter: EventEmitter<any> = new EventEmitter();
userEmitter: EventEmitter<any> = new EventEmitter();
static _() {
return new Session();
}
constructor(private configs: ConfigsService) {}
/**
* Return if loggedin, with an optional listener
......@@ -24,14 +23,14 @@ export class Session {
});
}
if (window.Minds.LoggedIn) return true;
if (this.configs.get('LoggedIn')) return true;
return false;
}
isAdmin() {
if (!this.isLoggedIn) return false;
if (window.Minds.Admin) return true;
if (this.configs.get('Admin')) return true;
return false;
}
......@@ -48,9 +47,11 @@ export class Session {
});
}
if (window.Minds.user) {
const user = this.configs.get('user');
if (user) {
// Attach user_guid to debug logs
return window.Minds.user;
return user;
}
return false;
......
import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpEvent,
HttpResponse,
} from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
import {
TransferState,
makeStateKey,
StateKey,
} from '@angular/platform-browser';
import { isPlatformServer } from '@angular/common';
@Injectable({
providedIn: 'root',
})
export class TransferHttpInterceptorService implements HttpInterceptor {
constructor(
private transferState: TransferState,
@Inject(PLATFORM_ID) private platformId: any
) {}
public intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (request.method !== 'GET') {
return next.handle(request);
}
const key: StateKey<string> = makeStateKey<string>(
request.url.split('/api').pop()
);
if (isPlatformServer(this.platformId)) {
return next.handle(request).pipe(
tap(event => {
this.transferState.set(key, (<HttpResponse<any>>event).body);
})
);
} else {
const storedResponse = this.transferState.get<any>(key, null);
if (storedResponse) {
const response = new HttpResponse({
body: storedResponse,
status: 200,
});
this.transferState.remove(key);
return of(response);
} else {
return next.handle(request);
}
}
}
}
......@@ -13,13 +13,13 @@ export class Sidebar {
drawer.classList.add('is-visible');
//we have a delay so we don't close after click
setTimeout(() => {
var listener = e => {
drawer.classList.remove('is-visible');
document.removeEventListener('click', listener);
};
document.addEventListener('click', listener);
}, 300);
// setTimeout(() => {
// var listener = e => {
// drawer.classList.remove('is-visible');
// document.removeEventListener('click', listener);
// };
// document.addEventListener('click', listener);
// }, 300);
}
close() {
var drawer: any = document.getElementsByTagName('minds-sidebar')[0];
......
<html>
<head>
<base href="/" />
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<title>Minds</title>
</head>
<body class="m-theme__light">
<!-- The app component created in app.ts -->
<m-app class="">
<div
class="mdl-progress mdl-progress__indeterminate initial-loading is-upgraded"
>
<div class="progressbar bar bar1" style="width: 0%;"></div>
<div class="bufferbar bar bar2" style="width: 100%;"></div>
<div class="auxbar bar bar3" style="width: 0%;"></div>
</div>
<div
class="m-initial-loading-centred"
style="width:100%; text-align:center; margin: 100px auto;"
>
<div
class="mdl-spinner mdl-spinner--single-color mdl-js-spinner is-active is-upgraded"
style="width: 64px;height: 64px;"
data-upgraded=",MaterialSpinner"
>
<div class="mdl-spinner__layer mdl-spinner__layer-1">
<div class="mdl-spinner__circle-clipper mdl-spinner__left">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__gap-patch">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__circle-clipper mdl-spinner__right">
<div class="mdl-spinner__circle"></div>
</div>
</div>
<div class="mdl-spinner__layer mdl-spinner__layer-2">
<div class="mdl-spinner__circle-clipper mdl-spinner__left">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__gap-patch">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__circle-clipper mdl-spinner__right">
<div class="mdl-spinner__circle"></div>
</div>
</div>
<div class="mdl-spinner__layer mdl-spinner__layer-3">
<div class="mdl-spinner__circle-clipper mdl-spinner__left">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__gap-patch">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__circle-clipper mdl-spinner__right">
<div class="mdl-spinner__circle"></div>
</div>
</div>
<div class="mdl-spinner__layer mdl-spinner__layer-4">
<div class="mdl-spinner__circle-clipper mdl-spinner__left">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__gap-patch">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__circle-clipper mdl-spinner__right">
<div class="mdl-spinner__circle"></div>
</div>
</div>
</div>
</div>
</m-app>
<script>
function module() {} // Fixes undefined module function in SystemJS bundle
</script>
<!-- shims:js -->
<!-- endinject -->
<script>
window.Minds = {
cdn_url: '/',
cdn_assets_url: '/',
};
</script>
</body>
</html>
<html>
<head>
<base href="/"/>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">
<title><?php echo $index->getTitle() ?></title>
</head>
<body class="m-theme__light">
<!-- The app component created in app.ts -->
<m-app class="">
<div class="mdl-progress mdl-progress__indeterminate initial-loading is-upgraded">
<div class="progressbar bar bar1" style="width: 0%;"></div>
<div class="bufferbar bar bar2" style="width: 100%;"></div>
<div class="auxbar bar bar3" style="width: 0%;"></div>
</div>
<div class="m-initial-loading-centred" style="width:100%; text-align:center; margin: 100px auto;">
<div class="mdl-spinner mdl-spinner--single-color mdl-js-spinner is-active is-upgraded"
style="width: 64px;height: 64px;" data-upgraded=",MaterialSpinner">
<div class="mdl-spinner__layer mdl-spinner__layer-1">
<div class="mdl-spinner__circle-clipper mdl-spinner__left">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__gap-patch">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__circle-clipper mdl-spinner__right">
<div class="mdl-spinner__circle"></div>
</div>
</div>
<div class="mdl-spinner__layer mdl-spinner__layer-2">
<div class="mdl-spinner__circle-clipper mdl-spinner__left">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__gap-patch">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__circle-clipper mdl-spinner__right">
<div class="mdl-spinner__circle"></div>
</div>
</div>
<div class="mdl-spinner__layer mdl-spinner__layer-3">
<div class="mdl-spinner__circle-clipper mdl-spinner__left">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__gap-patch">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__circle-clipper mdl-spinner__right">
<div class="mdl-spinner__circle"></div>
</div>
</div>
<div class="mdl-spinner__layer mdl-spinner__layer-4">
<div class="mdl-spinner__circle-clipper mdl-spinner__left">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__gap-patch">
<div class="mdl-spinner__circle"></div>
</div>
<div class="mdl-spinner__circle-clipper mdl-spinner__right">
<div class="mdl-spinner__circle"></div>
</div>
</div>
</div>
</div>
</m-app>
<script>
function module() {} // Fixes undefined module function in SystemJS bundle
</script>
<!-- shims:js -->
<!-- endinject -->
<script>
window.Minds = <!-- MINDS_GLOBALS -->;
</script>
</body>
</html>
import { enableProdMode } from '@angular/core';
import { environment } from './environments/environment';
// import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
// if (environment.production) {
// enableProdMode();
// }
export { AppServerModule } from './app/app.server.module';
......@@ -10,22 +10,22 @@ module.exports = {
mode: 'none',
entry: {
// This is our Express server for Dynamic universal
server: './socket-server.ts',
server: './server.ts',
// This is an example of Static prerendering (generative)
},
target: 'node',
resolve: { extensions: ['.ts', '.js'] },
// Make sure we include all node_modules etc
externals: [/node_modules/, nodeExternals({
whitelist: [
/^@agm\/core/,
/^hammerjs/
]
})],
externals: [
/node_modules/,
nodeExternals({
whitelist: [/^@agm\/core/, /^hammerjs/],
}),
],
output: {
// Puts the output at the root of the dist folder
path: path.join(__dirname, 'dist'),
filename: '[name].js'
filename: '[name].js',
},
module: {
rules: [
......@@ -36,7 +36,7 @@ module.exports = {
test: /[\/\\]@angular[\/\\]core[\/\\].+\.js$/,
parser: { system: true },
},
]
],
},
plugins: [
new webpack.ContextReplacementPlugin(
......@@ -53,11 +53,7 @@ module.exports = {
),
new NormalModuleReplacementPlugin(
/custom-event.js/,
path.resolve(__dirname, 'src/server-mocks/empty.js'),
path.resolve(__dirname, 'src/server-mocks/empty.js')
),
// new NormalModuleReplacementPlugin(
// /dexie.js/,
// path.resolve(__dirname, 'src/server-mocks/empty.js'),
// ),
]
}
],
};
Please register or to comment