diff --git a/angular.json b/angular.json index 0d9f76b..987cd93 100644 --- a/angular.json +++ b/angular.json @@ -479,6 +479,123 @@ } } }, + "frontend-theming": { + "projectType": "application", + "schematics": { + "@nrwl/angular:component": { + "style": "scss", + "changeDetection": "OnPush" + } + }, + "root": "apps/frontend/theming", + "sourceRoot": "apps/frontend/theming/src", + "prefix": "medium-stories", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/apps/frontend/theming/browser", + "index": "apps/frontend/theming/src/index.html", + "main": "apps/frontend/theming/src/main.browser.ts", + "polyfills": "apps/frontend/theming/src/polyfills.ts", + "tsConfig": "apps/frontend/theming/tsconfig.app.json", + "aot": false, + "assets": ["apps/frontend/theming/src/favicon.ico", "apps/frontend/theming/src/assets"], + "styles": ["apps/frontend/theming/src/styles.scss"], + "stylePreprocessorOptions": { + "includePaths": ["apps/frontend/theming/src/styles"] + }, + "scripts": [] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "apps/frontend/theming/src/environments/environment.ts", + "with": "apps/frontend/theming/src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "extractCss": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "2mb", + "maximumError": "5mb" + } + ] + } + } + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "frontend-theming:build" + }, + "configurations": { + "production": { + "browserTarget": "frontend-theming:build:production" + } + } + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "frontend-theming:build" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["apps/frontend/theming/tsconfig.app.json", "apps/frontend/theming/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "!apps/frontend/theming/**"] + } + }, + "test": { + "builder": "@nrwl/jest:jest", + "options": { + "jestConfig": "apps/frontend/theming/jest.config.js", + "tsConfig": "apps/frontend/theming/tsconfig.spec.json", + "setupFile": "apps/frontend/theming/src/test-setup.ts" + } + }, + "server": { + "builder": "@angular-devkit/build-angular:server", + "options": { + "bundleDependencies": "none", + "outputPath": "dist/apps/frontend/theming/server", + "main": "apps/frontend/theming/src/main.server.ts", + "tsConfig": "apps/frontend/theming/tsconfig.server.json", + "stylePreprocessorOptions": { + "includePaths": ["apps/frontend/theming/src/styles"] + } + }, + "configurations": { + "production": { + "bundleDependencies": "all", + "fileReplacements": [ + { + "replace": "apps/frontend/theming/src/environments/environment.ts", + "with": "apps/frontend/theming/src/environments/environment.prod.ts" + } + ], + "sourceMap": false, + "optimization": { + "scripts": false, + "styles": true + } + } + } + } + } + }, "frontend-translation": { "projectType": "application", "schematics": { diff --git a/apps/frontend/theming/browserslist b/apps/frontend/theming/browserslist new file mode 100644 index 0000000..8084853 --- /dev/null +++ b/apps/frontend/theming/browserslist @@ -0,0 +1,12 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +> 0.5% +last 2 versions +Firefox ESR +not dead +not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/apps/frontend/theming/jest.config.js b/apps/frontend/theming/jest.config.js new file mode 100644 index 0000000..6129368 --- /dev/null +++ b/apps/frontend/theming/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + name: 'frontend-base', + preset: '../../../jest.config.js', + coverageDirectory: '../../../coverage/apps/frontend/base', + snapshotSerializers: ['jest-preset-angular/AngularSnapshotSerializer.js', 'jest-preset-angular/HTMLCommentSerializer.js'] +}; diff --git a/apps/frontend/theming/server.ts b/apps/frontend/theming/server.ts new file mode 100644 index 0000000..9c2c5d5 --- /dev/null +++ b/apps/frontend/theming/server.ts @@ -0,0 +1,82 @@ +/** + * *** NOTE ON IMPORTING FROM ANGULAR AND NGUNIVERSAL IN THIS FILE *** + * + * If your application uses third-party dependencies, you'll need to + * either use Webpack or the Angular CLI's `bundleDependencies` feature + * in order to adequately package them for use on the server without a + * node_modules directory. + * + * However, due to the nature of the CLI's `bundleDependencies`, importing + * Angular in this file will create a different instance of Angular than + * the version in the compiled application code. This leads to unavoidable + * conflicts. Therefore, please do not explicitly import from @angular or + * @nguniversal in this file. You can export any needed resources + * from your application's main.server.ts file, as seen below with the + * import for `ngExpressEngine`. + */ +import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens'; +import 'zone.js/dist/zone-node'; + +import * as express from 'express'; +import { join } from 'path'; + +// Express server +const app = express(); + +const PORT = process.env.PORT || 4000; +const DIST_FOLDER = join(process.cwd(), 'dist', 'apps'); +const appName = 'frontend/theming'; +const appBrowser = join(DIST_FOLDER, appName, 'browser'); + +// * NOTE :: leave this as require() since this file is built Dynamically from webpack +const { + AppServerModuleNgFactory, + LAZY_MODULE_MAP, + ngExpressEngine, + provideModuleMap +} = require('../../../dist/apps/frontend/theming/server/main'); + +// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine) +app.engine( + 'html', + ngExpressEngine({ + bootstrap: AppServerModuleNgFactory, + providers: [provideModuleMap(LAZY_MODULE_MAP)] + }) +); + +app.set('view engine', 'html'); +app.set('views', appBrowser); + +// Example Express Rest API endpoints +// app.get('/api/**', (req, res) => { }); +// Serve static files from /browser +app.get( + '*.*', + express.static(appBrowser, { + maxAge: '1y' + }) +); + +// All regular routes use the Universal engine +app.get('*', (req, res) => { + res.render(join(appBrowser, 'index.html'), { + req, + res, + providers: [ + { + provide: REQUEST, + useValue: req + }, + { + provide: RESPONSE, + useValue: res + } + ] + }); +}); + +// Start up the Node server +app.listen(PORT, () => { + console.log(`Node Express server listening on http://localhost:${PORT}`); +}); diff --git a/apps/frontend/theming/src/app/app.browser.module.spec.ts b/apps/frontend/theming/src/app/app.browser.module.spec.ts new file mode 100644 index 0000000..e0a28ce --- /dev/null +++ b/apps/frontend/theming/src/app/app.browser.module.spec.ts @@ -0,0 +1,15 @@ +import { async, TestBed } from '@angular/core/testing'; + +import { AppBrowserModule } from './app.browser.module'; + +describe('AppBrowserModule', () => { + beforeEach(async(() => { + return TestBed.configureTestingModule({ + imports: [AppBrowserModule] + }).compileComponents(); + })); + + it('should create', () => { + expect(AppBrowserModule).toBeTruthy(); + }); +}); diff --git a/apps/frontend/theming/src/app/app.browser.module.ts b/apps/frontend/theming/src/app/app.browser.module.ts new file mode 100644 index 0000000..46b29be --- /dev/null +++ b/apps/frontend/theming/src/app/app.browser.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreDevtoolsModule } from '@ngrx/store-devtools'; + +import { BrowserStorageModule } from '@medium-stories/storage'; +import { BrowserTranslationModule } from '@medium-stories/translation'; + +import { AppModule } from './app.module'; +import { CoreModule } from './core/core.module'; +import { AppComponent } from './core/containers/app/app.component'; +import { environment } from '../environments/environment'; + +@NgModule({ + imports: [ + AppModule, + CoreModule, + BrowserAnimationsModule, + BrowserStorageModule.forRoot(), + BrowserTranslationModule.forRoot({ + config: environment.translation + }), + !environment.production ? StoreDevtoolsModule.instrument({ logOnly: environment.production }) : [] + ], + bootstrap: [AppComponent] +}) +export class AppBrowserModule {} diff --git a/apps/frontend/theming/src/app/app.module.spec.ts b/apps/frontend/theming/src/app/app.module.spec.ts new file mode 100644 index 0000000..0264634 --- /dev/null +++ b/apps/frontend/theming/src/app/app.module.spec.ts @@ -0,0 +1,15 @@ +import { async, TestBed } from '@angular/core/testing'; + +import { AppModule } from './app.module'; + +describe('AppModule', () => { + beforeEach(async(() => { + return TestBed.configureTestingModule({ + imports: [AppModule] + }).compileComponents(); + })); + + it('should create', () => { + expect(AppModule).toBeTruthy(); + }); +}); diff --git a/apps/frontend/theming/src/app/app.module.ts b/apps/frontend/theming/src/app/app.module.ts new file mode 100644 index 0000000..cc56ce6 --- /dev/null +++ b/apps/frontend/theming/src/app/app.module.ts @@ -0,0 +1,22 @@ +import { HttpClientModule } from '@angular/common/http'; +import { NgModule } from '@angular/core'; +import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser'; +import { TransferHttpCacheModule } from '@nguniversal/common'; + +import { APP_DIST } from '@medium-stories/common'; + +@NgModule({ + imports: [ + BrowserModule.withServerTransition({ appId: 'medium-stories' }), + HttpClientModule, + BrowserTransferStateModule, + TransferHttpCacheModule + ], + providers: [ + { + provide: APP_DIST, // need to get translation path on Universal + useValue: 'frontend/translation' + } + ] +}) +export class AppModule {} diff --git a/apps/frontend/theming/src/app/app.server.module.spec.ts b/apps/frontend/theming/src/app/app.server.module.spec.ts new file mode 100644 index 0000000..ab4ca46 --- /dev/null +++ b/apps/frontend/theming/src/app/app.server.module.spec.ts @@ -0,0 +1,15 @@ +import { async, TestBed } from '@angular/core/testing'; + +import { AppServerModule } from './app.server.module'; + +describe('AppServerModule', () => { + beforeEach(async(() => { + return TestBed.configureTestingModule({ + imports: [AppServerModule] + }).compileComponents(); + })); + + it('should create', () => { + expect(AppServerModule).toBeTruthy(); + }); +}); diff --git a/apps/frontend/theming/src/app/app.server.module.ts b/apps/frontend/theming/src/app/app.server.module.ts new file mode 100644 index 0000000..9071026 --- /dev/null +++ b/apps/frontend/theming/src/app/app.server.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; +import { ServerModule, ServerTransferStateModule } from '@angular/platform-server'; +import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader'; + +import { ServerStorageModule } from '@medium-stories/storage'; +import { ServerTranslationModule } from '@medium-stories/translation'; + +import { environment } from '../environments/environment'; +import { AppModule } from './app.module'; +import { CoreModule } from './core/core.module'; +import { AppComponent } from './core/containers/app/app.component'; + +@NgModule({ + imports: [ + AppModule, + CoreModule, + ServerStorageModule.forRoot(), + ServerModule, + ServerTranslationModule.forRoot({ + config: environment.translation + }), + ModuleMapLoaderModule, + ServerTransferStateModule + ], + bootstrap: [AppComponent] +}) +export class AppServerModule {} diff --git a/apps/frontend/theming/src/app/core/containers/app/app.component.html b/apps/frontend/theming/src/app/core/containers/app/app.component.html new file mode 100644 index 0000000..0680b43 --- /dev/null +++ b/apps/frontend/theming/src/app/core/containers/app/app.component.html @@ -0,0 +1 @@ + diff --git a/apps/frontend/theming/src/app/core/containers/app/app.component.scss b/apps/frontend/theming/src/app/core/containers/app/app.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/apps/frontend/theming/src/app/core/containers/app/app.component.spec.ts b/apps/frontend/theming/src/app/core/containers/app/app.component.spec.ts new file mode 100644 index 0000000..c1b44e7 --- /dev/null +++ b/apps/frontend/theming/src/app/core/containers/app/app.component.spec.ts @@ -0,0 +1,26 @@ +import { TestBed, async, ComponentFixture } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + let component: AppComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + return TestBed.configureTestingModule({ + imports: [RouterTestingModule], + declarations: [AppComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AppComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/frontend/theming/src/app/core/containers/app/app.component.ts b/apps/frontend/theming/src/app/core/containers/app/app.component.ts new file mode 100644 index 0000000..6222ee5 --- /dev/null +++ b/apps/frontend/theming/src/app/core/containers/app/app.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; + +import { TranslationFacade } from '@medium-stories/translation'; + +@Component({ + selector: 'medium-stories-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'] +}) +export class AppComponent { + constructor(private translationFacade: TranslationFacade) { + this.translationFacade.init(); + } +} diff --git a/apps/frontend/theming/src/app/core/core.common.ts b/apps/frontend/theming/src/app/core/core.common.ts new file mode 100644 index 0000000..249ae2a --- /dev/null +++ b/apps/frontend/theming/src/app/core/core.common.ts @@ -0,0 +1,12 @@ +import { Routes } from '@angular/router'; + +import { AppComponent } from './containers/app/app.component'; + +export const coreContainers: any[] = [AppComponent]; + +export const coreRoutes: Routes = [ + { + path: '', + loadChildren: () => import('../home/home.module').then(m => m.HomeModule) + } +]; diff --git a/apps/frontend/theming/src/app/core/core.module.spec.ts b/apps/frontend/theming/src/app/core/core.module.spec.ts new file mode 100644 index 0000000..11f9b03 --- /dev/null +++ b/apps/frontend/theming/src/app/core/core.module.spec.ts @@ -0,0 +1,15 @@ +import { async, TestBed } from '@angular/core/testing'; + +import { CoreModule } from './core.module'; + +describe('CoreModule', () => { + beforeEach(async(() => { + return TestBed.configureTestingModule({ + imports: [CoreModule] + }).compileComponents(); + })); + + it('should create', () => { + expect(CoreModule).toBeTruthy(); + }); +}); diff --git a/apps/frontend/theming/src/app/core/core.module.ts b/apps/frontend/theming/src/app/core/core.module.ts new file mode 100644 index 0000000..1250ac5 --- /dev/null +++ b/apps/frontend/theming/src/app/core/core.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { TranslateModule } from '@ngx-translate/core'; +import { NxModule } from '@nrwl/angular'; + +import { RootStoreModule } from '@medium-stories/store'; + +import { coreContainers, coreRoutes } from './core.common'; + +@NgModule({ + imports: [ + CommonModule, + NxModule.forRoot(), + RouterModule.forRoot(coreRoutes, { initialNavigation: 'enabled' }), + RootStoreModule, + TranslateModule + ], + declarations: [...coreContainers] +}) +export class CoreModule {} diff --git a/apps/frontend/theming/src/app/home/containers/home/home.component.html b/apps/frontend/theming/src/app/home/containers/home/home.component.html new file mode 100644 index 0000000..28441dd --- /dev/null +++ b/apps/frontend/theming/src/app/home/containers/home/home.component.html @@ -0,0 +1,16 @@ +

{{ 'home.title' | translate }}

+ +
+
+ / + +
+ +
{{ 'home.count' | translate }}: {{ counter }}
+ +
+ +
+
diff --git a/apps/frontend/theming/src/app/home/containers/home/home.component.scss b/apps/frontend/theming/src/app/home/containers/home/home.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/apps/frontend/theming/src/app/home/containers/home/home.component.spec.ts b/apps/frontend/theming/src/app/home/containers/home/home.component.spec.ts new file mode 100644 index 0000000..95eb96f --- /dev/null +++ b/apps/frontend/theming/src/app/home/containers/home/home.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; + +describe('HomeComponent', () => { + let component: HomeComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + return TestBed.configureTestingModule({ + declarations: [HomeComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/frontend/theming/src/app/home/containers/home/home.component.ts b/apps/frontend/theming/src/app/home/containers/home/home.component.ts new file mode 100644 index 0000000..f708cfc --- /dev/null +++ b/apps/frontend/theming/src/app/home/containers/home/home.component.ts @@ -0,0 +1,35 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; + +import { CookieStorage } from '@medium-stories/storage'; +import { TranslationFacade } from '@medium-stories/translation'; + +@Component({ + selector: 'medium-stories-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class HomeComponent { + /** + * Counter storage key + */ + static readonly counterKey = 'myCounter'; + + /** + * Counter + */ + counter: number; + + constructor(public translationFacade: TranslationFacade, private cookieStorage: CookieStorage) { + const savedCounter = this.cookieStorage.getItem(HomeComponent.counterKey); + this.counter = savedCounter ? +savedCounter : 0; + } + + /** + * To increment the counter + */ + add(): void { + this.counter++; + this.cookieStorage.setItem(HomeComponent.counterKey, this.counter.toString()); + } +} diff --git a/apps/frontend/theming/src/app/home/home.common.ts b/apps/frontend/theming/src/app/home/home.common.ts new file mode 100644 index 0000000..35490b4 --- /dev/null +++ b/apps/frontend/theming/src/app/home/home.common.ts @@ -0,0 +1,20 @@ +import { Routes } from '@angular/router'; + +import { BaseLayoutComponent } from '@medium-stories/layouts'; + +import { HomeComponent } from './containers/home/home.component'; + +export const homeContainers: any[] = [HomeComponent]; + +export const homeRoutes: Routes = [ + { + path: '', + component: BaseLayoutComponent, + children: [ + { + path: '', + component: HomeComponent + } + ] + } +]; diff --git a/apps/frontend/theming/src/app/home/home.module.spec.ts b/apps/frontend/theming/src/app/home/home.module.spec.ts new file mode 100644 index 0000000..b4fbe29 --- /dev/null +++ b/apps/frontend/theming/src/app/home/home.module.spec.ts @@ -0,0 +1,15 @@ +import { async, TestBed } from '@angular/core/testing'; + +import { HomeModule } from './home.module'; + +describe('HomeModule', () => { + beforeEach(async(() => { + return TestBed.configureTestingModule({ + imports: [HomeModule] + }).compileComponents(); + })); + + it('should create', () => { + expect(HomeModule).toBeTruthy(); + }); +}); diff --git a/apps/frontend/theming/src/app/home/home.module.ts b/apps/frontend/theming/src/app/home/home.module.ts new file mode 100644 index 0000000..54938cc --- /dev/null +++ b/apps/frontend/theming/src/app/home/home.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { TranslateModule } from '@ngx-translate/core'; + +import { LayoutsModule } from '@medium-stories/layouts'; + +import { homeContainers, homeRoutes } from './home.common'; + +@NgModule({ + imports: [CommonModule, RouterModule.forChild(homeRoutes), LayoutsModule, TranslateModule], + declarations: [...homeContainers] +}) +export class HomeModule {} diff --git a/apps/frontend/theming/src/assets/.gitkeep b/apps/frontend/theming/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/frontend/theming/src/assets/i18n/en.json b/apps/frontend/theming/src/assets/i18n/en.json new file mode 100644 index 0000000..49b9055 --- /dev/null +++ b/apps/frontend/theming/src/assets/i18n/en.json @@ -0,0 +1,13 @@ +{ + "languages": { + "ru": "Русский", + "en": "English" + }, + "home": { + "title": "Home page with Theming", + "count": "Count", + "actions": { + "add": "Add" + } + } +} diff --git a/apps/frontend/theming/src/assets/i18n/ru.json b/apps/frontend/theming/src/assets/i18n/ru.json new file mode 100644 index 0000000..4793fbd --- /dev/null +++ b/apps/frontend/theming/src/assets/i18n/ru.json @@ -0,0 +1,13 @@ +{ + "languages": { + "ru": "Русский", + "en": "English" + }, + "home": { + "title": "Главная страница с Theming", + "count": "Количество", + "actions": { + "add": "Добавить" + } + } +} diff --git a/apps/frontend/theming/src/environments/environment.prod.ts b/apps/frontend/theming/src/environments/environment.prod.ts new file mode 100644 index 0000000..3cd5a3f --- /dev/null +++ b/apps/frontend/theming/src/environments/environment.prod.ts @@ -0,0 +1,7 @@ +export const environment = { + production: true, + translation: { + languages: ['ru', 'en'], + language: 'ru' + } +}; diff --git a/apps/frontend/theming/src/environments/environment.ts b/apps/frontend/theming/src/environments/environment.ts new file mode 100644 index 0000000..f8bd51e --- /dev/null +++ b/apps/frontend/theming/src/environments/environment.ts @@ -0,0 +1,20 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false, + translation: { + languages: ['ru', 'en'], + language: 'ru' + } +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git a/apps/frontend/theming/src/favicon.ico b/apps/frontend/theming/src/favicon.ico new file mode 100644 index 0000000..8081c7c Binary files /dev/null and b/apps/frontend/theming/src/favicon.ico differ diff --git a/apps/frontend/theming/src/index.html b/apps/frontend/theming/src/index.html new file mode 100644 index 0000000..5f50c8d --- /dev/null +++ b/apps/frontend/theming/src/index.html @@ -0,0 +1,14 @@ + + + + + Frontend Theming + + + + + + + + + diff --git a/apps/frontend/theming/src/main.browser.ts b/apps/frontend/theming/src/main.browser.ts new file mode 100644 index 0000000..9f5d6d6 --- /dev/null +++ b/apps/frontend/theming/src/main.browser.ts @@ -0,0 +1,15 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppBrowserModule } from './app/app.browser.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +document.addEventListener('DOMContentLoaded', () => { + platformBrowserDynamic() + .bootstrapModule(AppBrowserModule) + .catch(err => console.error(err)); +}); diff --git a/apps/frontend/theming/src/main.server.ts b/apps/frontend/theming/src/main.server.ts new file mode 100644 index 0000000..33ed185 --- /dev/null +++ b/apps/frontend/theming/src/main.server.ts @@ -0,0 +1,11 @@ +import { enableProdMode } from '@angular/core'; + +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +export { AppServerModule } from './app/app.server.module'; +export { ngExpressEngine } from '@nguniversal/express-engine'; +export { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; diff --git a/apps/frontend/theming/src/polyfills.ts b/apps/frontend/theming/src/polyfills.ts new file mode 100644 index 0000000..2f258e5 --- /dev/null +++ b/apps/frontend/theming/src/polyfills.ts @@ -0,0 +1,62 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + */ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags.ts'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git a/apps/frontend/theming/src/styles.scss b/apps/frontend/theming/src/styles.scss new file mode 100644 index 0000000..8850fcc --- /dev/null +++ b/apps/frontend/theming/src/styles.scss @@ -0,0 +1,2 @@ +/* You can add global styles to this file, and also import other style files */ +@import 'styles/style'; diff --git a/apps/frontend/theming/src/styles/style.scss b/apps/frontend/theming/src/styles/style.scss new file mode 100644 index 0000000..922fc0b --- /dev/null +++ b/apps/frontend/theming/src/styles/style.scss @@ -0,0 +1,5 @@ +// Import app utils +@import './utils'; + +// Import common styles +@import 'styles/style'; diff --git a/apps/frontend/theming/src/styles/utils.scss b/apps/frontend/theming/src/styles/utils.scss new file mode 100644 index 0000000..f3c30be --- /dev/null +++ b/apps/frontend/theming/src/styles/utils.scss @@ -0,0 +1,6 @@ +// Import common utils +@import 'styles/utils'; + +// Override common variables and mixins +$default-bg: #300a24; +$default-color: #fff; diff --git a/apps/frontend/theming/src/test-setup.ts b/apps/frontend/theming/src/test-setup.ts new file mode 100644 index 0000000..8d88704 --- /dev/null +++ b/apps/frontend/theming/src/test-setup.ts @@ -0,0 +1 @@ +import 'jest-preset-angular'; diff --git a/apps/frontend/theming/tsconfig.app.json b/apps/frontend/theming/tsconfig.app.json new file mode 100644 index 0000000..dcfd98e --- /dev/null +++ b/apps/frontend/theming/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "include": ["**/*.ts"], + "exclude": ["src/test-setup.ts", "**/*.spec.ts"] +} diff --git a/apps/frontend/theming/tsconfig.json b/apps/frontend/theming/tsconfig.json new file mode 100644 index 0000000..08c7db8 --- /dev/null +++ b/apps/frontend/theming/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "types": ["node", "jest"] + }, + "include": ["**/*.ts"] +} diff --git a/apps/frontend/theming/tsconfig.server.json b/apps/frontend/theming/tsconfig.server.json new file mode 100644 index 0000000..7c88d7d --- /dev/null +++ b/apps/frontend/theming/tsconfig.server.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.app.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc-server" + }, + "angularCompilerOptions": { + "entryModule": "./src/app/app.server.module#AppServerModule" + } +} diff --git a/apps/frontend/theming/tsconfig.spec.json b/apps/frontend/theming/tsconfig.spec.json new file mode 100644 index 0000000..fd405a6 --- /dev/null +++ b/apps/frontend/theming/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] +} diff --git a/apps/frontend/theming/tslint.json b/apps/frontend/theming/tslint.json new file mode 100644 index 0000000..0e80ef4 --- /dev/null +++ b/apps/frontend/theming/tslint.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tslint.json", + "rules": { + "directive-selector": [true, "attribute", "mediumStories", "camelCase"], + "component-selector": [true, "element", "medium-stories", "kebab-case"] + } +} diff --git a/apps/frontend/theming/webpack.ssr.config.js b/apps/frontend/theming/webpack.ssr.config.js new file mode 100644 index 0000000..c641286 --- /dev/null +++ b/apps/frontend/theming/webpack.ssr.config.js @@ -0,0 +1,55 @@ +// Work around for https://github.com/angular/angular-cli/issues/7200 + +const path = require('path'); +const webpack = require('webpack'); + +const distFolder = path.join(process.cwd(), 'dist'); +const appsFolder = path.join(process.cwd(), 'apps'); +const appName = 'frontend/theming'; + +module.exports = { + mode: 'none', + entry: { + // This is our Express server for Dynamic universal + server: path.join(appsFolder, appName, 'server.ts') + }, + externals: { + '../../../dist/apps/frontend/theming/server/main': 'require("./server/main")' + }, + target: 'node', + resolve: { extensions: ['.ts', '.js'] }, + optimization: { + minimize: false + }, + output: { + // Puts the output at the root of the dist folder + path: path.join(distFolder, 'apps', appName), + filename: '[name].js' + }, + module: { + noParse: /polyfills-.*\.js/, + rules: [ + { test: /\.ts$/, loader: 'ts-loader' }, + { + // Mark files inside `@angular/core` as using SystemJS style dynamic imports. + // Removing this will cause deprecation warnings to appear. + test: /(\\|\/)@angular(\\|\/)core(\\|\/).+\.js$/, + parser: { system: true } + } + ] + }, + plugins: [ + new webpack.ContextReplacementPlugin( + // fixes WARNING Critical dependency: the request of a dependency is an expression + /(.+)?angular(\\|\/)core(.+)?/, + path.join(distFolder, 'apps', appName), // location of your src + {} // a map of your routes + ), + new webpack.ContextReplacementPlugin( + // fixes WARNING Critical dependency: the request of a dependency is an expression + /(.+)?express(\\|\/)(.+)?/, + path.join(distFolder, 'apps', appName), + {} + ) + ] +}; diff --git a/nx.json b/nx.json index d026bb3..4981523 100644 --- a/nx.json +++ b/nx.json @@ -23,6 +23,9 @@ "frontend-store": { "tags": [] }, + "frontend-theming": { + "tags": [] + }, "frontend-translation": { "tags": [] }, diff --git a/package.json b/package.json index 7494efe..94f6f60 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,9 @@ "build:ssr:translation-state": "ng build frontend-translation-state --prod && ng run frontend-translation-state:server:production && webpack --config apps/frontend/translation-state/webpack.ssr.config.js --progress --colors", "serve:ssr:translation-state": "node dist/apps/frontend/translation-state/server.js", "build:ssr:common-styles": "ng build frontend-common-styles --prod && ng run frontend-common-styles:server:production && webpack --config apps/frontend/common-styles/webpack.ssr.config.js --progress --colors", - "serve:ssr:common-styles": "node dist/apps/frontend/common-styles/server.js" + "serve:ssr:common-styles": "node dist/apps/frontend/common-styles/server.js", + "build:ssr:theming": "ng build frontend-theming --prod && ng run frontend-theming:server:production && webpack --config apps/frontend/theming/webpack.ssr.config.js --progress --colors", + "serve:ssr:theming": "node dist/apps/frontend/theming/server.js" }, "private": true, "dependencies": { @@ -71,6 +73,7 @@ "bootstrap": "^4.3.1", "core-js": "^2.6.9", "express": "^4.17.1", + "hammerjs": "^2.0.8", "ngx-cookie": "^4.1.2", "rxjs": "~6.5.2", "zone.js": "^0.9.1" diff --git a/yarn.lock b/yarn.lock index d8d2ceb..c40dc41 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4442,6 +4442,11 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= +hammerjs@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" + integrity sha1-BO93hiz/K7edMPdpIJWTAiK/YPE= + handle-thing@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754"