ssr wip
1
.gitignore
vendored
@@ -8,6 +8,7 @@ db.sqlite3
|
|||||||
__pycache__
|
__pycache__
|
||||||
.vscode
|
.vscode
|
||||||
build
|
build
|
||||||
|
build-server
|
||||||
*.d.ts
|
*.d.ts
|
||||||
.env
|
.env
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
|
|||||||
0
build-server/.gitkeep
Normal file
12
package.json
@@ -18,6 +18,7 @@
|
|||||||
"@angular/forms": "^11.0.0",
|
"@angular/forms": "^11.0.0",
|
||||||
"@angular/platform-browser": "^11.0.0",
|
"@angular/platform-browser": "^11.0.0",
|
||||||
"@angular/platform-browser-dynamic": "^11.0.0",
|
"@angular/platform-browser-dynamic": "^11.0.0",
|
||||||
|
"@angular/platform-server": "^11.2.14",
|
||||||
"@angular/router": "^11.0.0",
|
"@angular/router": "^11.0.0",
|
||||||
"@fontsource/fira-code": "^4.5.0",
|
"@fontsource/fira-code": "^4.5.0",
|
||||||
"@fortawesome/angular-fontawesome": "0.8",
|
"@fortawesome/angular-fontawesome": "0.8",
|
||||||
@@ -40,6 +41,7 @@
|
|||||||
"html-loader": "^2.1.2",
|
"html-loader": "^2.1.2",
|
||||||
"html-webpack-plugin": "^5.3.2",
|
"html-webpack-plugin": "^5.3.2",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
|
"mini-css-extract-plugin": "^2.1.0",
|
||||||
"ngx-toastr": "^14.0.0",
|
"ngx-toastr": "^14.0.0",
|
||||||
"node-sass": "^6.0.0",
|
"node-sass": "^6.0.0",
|
||||||
"pug": "^3.0.2",
|
"pug": "^3.0.2",
|
||||||
@@ -57,8 +59,16 @@
|
|||||||
"typescript": "~4.1",
|
"typescript": "~4.1",
|
||||||
"val-loader": "^4.0.0",
|
"val-loader": "^4.0.0",
|
||||||
"webpack": "^5.38.1",
|
"webpack": "^5.38.1",
|
||||||
|
"webpack-bundle-analyzer": "^4.4.2",
|
||||||
"webpack-cli": "^4.7.2",
|
"webpack-cli": "^4.7.2",
|
||||||
"zone.js": "^0.11.4"
|
"zone.js": "^0.11.4"
|
||||||
},
|
},
|
||||||
"dependencies": {}
|
"dependencies": {
|
||||||
|
"@nguniversal/express-engine": "^11.1.0",
|
||||||
|
"domino": "^2.1.6",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"express-http-proxy": "^1.6.2",
|
||||||
|
"mock-browser": "^0.92.14",
|
||||||
|
"source-map-support": "^0.5.19"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { FormsModule } from '@angular/forms'
|
|||||||
import { RouterModule } from '@angular/router'
|
import { RouterModule } from '@angular/router'
|
||||||
import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http'
|
import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http'
|
||||||
import { ClipboardModule } from '@angular/cdk/clipboard'
|
import { ClipboardModule } from '@angular/cdk/clipboard'
|
||||||
|
import { TransferHttpCacheModule } from '@nguniversal/common'
|
||||||
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'
|
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'
|
||||||
import { AppComponent } from './components/app.component'
|
import { AppComponent } from './components/app.component'
|
||||||
import { MainComponent } from './components/main.component'
|
import { MainComponent } from './components/main.component'
|
||||||
@@ -16,6 +17,8 @@ import { HomeComponent } from './components/home.component'
|
|||||||
import { LoginComponent } from './components/login.component'
|
import { LoginComponent } from './components/login.component'
|
||||||
import { InstanceInfoResolver } from './api'
|
import { InstanceInfoResolver } from './api'
|
||||||
|
|
||||||
|
import '@fortawesome/fontawesome-svg-core/styles.css'
|
||||||
|
|
||||||
const ROUTES = [
|
const ROUTES = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
@@ -42,7 +45,10 @@ const ROUTES = [
|
|||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule.withServerTransition({
|
||||||
|
appId: 'tabby'
|
||||||
|
}),
|
||||||
|
TransferHttpCacheModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
|
|||||||
27
src/app.server.module.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { HTTP_INTERCEPTORS } from '@angular/common/http'
|
||||||
|
import { NgModule } from '@angular/core'
|
||||||
|
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server'
|
||||||
|
import { AppModule } from './app.module'
|
||||||
|
import { AppComponent } from './components/app.component'
|
||||||
|
import { UniversalInterceptor } from './ssr-interceptor'
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
// The AppServerModule should import your AppModule followed
|
||||||
|
// by the ServerModule from @angular/platform-server.
|
||||||
|
AppModule,
|
||||||
|
ServerModule,
|
||||||
|
ServerTransferStateModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
useClass: UniversalInterceptor,
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// Since the bootstrapped component is not inherited from your
|
||||||
|
// imported AppModule, it needs to be repeated here.
|
||||||
|
bootstrap: [AppComponent],
|
||||||
|
})
|
||||||
|
export class AppServerModule {}
|
||||||
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 909 KiB |
|
Before Width: | Height: | Size: 536 KiB After Width: | Height: | Size: 260 KiB |
|
Before Width: | Height: | Size: 371 KiB After Width: | Height: | Size: 314 KiB |
|
Before Width: | Height: | Size: 666 KiB After Width: | Height: | Size: 383 KiB |
|
Before Width: | Height: | Size: 585 KiB After Width: | Height: | Size: 390 KiB |
|
Before Width: | Height: | Size: 728 KiB After Width: | Height: | Size: 614 KiB |
@@ -49,7 +49,7 @@
|
|||||||
.container
|
.container
|
||||||
.row
|
.row
|
||||||
.col-12.col-xl-6
|
.col-12.col-xl-6
|
||||||
img.screenshot([src]='screenshots.window')
|
img.screenshot([src]='screenshots.window', loading='lazy')
|
||||||
.col-12.col-xl-6
|
.col-12.col-xl-6
|
||||||
h1 The important stuff
|
h1 The important stuff
|
||||||
ul
|
ul
|
||||||
@@ -79,13 +79,13 @@
|
|||||||
li Optional #[strong global hotkey] to focus/hide the terminal
|
li Optional #[strong global hotkey] to focus/hide the terminal
|
||||||
li Bracketed paste
|
li Bracketed paste
|
||||||
.col-12.col-xl-6
|
.col-12.col-xl-6
|
||||||
img.screenshot([src]='screenshots.tabs')
|
img.screenshot([src]='screenshots.tabs', loading='lazy')
|
||||||
|
|
||||||
.section.section-a
|
.section.section-a
|
||||||
.container
|
.container
|
||||||
.row
|
.row
|
||||||
.col-12.col-xl-6
|
.col-12.col-xl-6
|
||||||
img.screenshot([src]='screenshots.ssh')
|
img.screenshot([src]='screenshots.ssh', loading='lazy')
|
||||||
.col-12.col-xl-6
|
.col-12.col-xl-6
|
||||||
h1 SSH Client
|
h1 SSH Client
|
||||||
ul
|
ul
|
||||||
@@ -110,13 +110,13 @@
|
|||||||
li Optional #[strong portable mode]
|
li Optional #[strong portable mode]
|
||||||
li Current directory detection that works
|
li Current directory detection that works
|
||||||
.col-12.col-xl-6
|
.col-12.col-xl-6
|
||||||
img.screenshot([src]='screenshots.win')
|
img.screenshot([src]='screenshots.win', loading='lazy')
|
||||||
|
|
||||||
.section.section-a
|
.section.section-a
|
||||||
.container
|
.container
|
||||||
.row
|
.row
|
||||||
.col-12.col-xl-6
|
.col-12.col-xl-6
|
||||||
img.screenshot([src]='screenshots.serial')
|
img.screenshot([src]='screenshots.serial', loading='lazy')
|
||||||
.col-12.col-xl-6
|
.col-12.col-xl-6
|
||||||
h1 Serial Terminal
|
h1 Serial Terminal
|
||||||
ul
|
ul
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="initial-scale=1, minimal-ui, shrink-to-fit=no">
|
<meta name="viewport" content="initial-scale=1, minimal-ui, shrink-to-fit=no">
|
||||||
<link rel="icon" type="image/png">
|
<link rel="icon" type="image/png">
|
||||||
<link rel="shortcut icon" type="image/png" href="./assets/favicon.png">
|
<link rel="shortcut icon" type="image/png" href="./assets/favicon.png">
|
||||||
|
|||||||
2
src/index.server.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import './styles.scss'
|
||||||
|
export { AppServerModule } from './app.server.module'
|
||||||
@@ -10,6 +10,5 @@ import './styles.scss'
|
|||||||
import { AppModule } from './app.module'
|
import { AppModule } from './app.module'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enableProdMode()
|
enableProdMode()
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||||
|
|||||||
53
src/server.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import 'zone.js/dist/zone-node';
|
||||||
|
import { enableProdMode } from '@angular/core';
|
||||||
|
|
||||||
|
require('source-map-support').install()
|
||||||
|
|
||||||
|
// Express Engine
|
||||||
|
import { ngExpressEngine } from '@nguniversal/express-engine';
|
||||||
|
|
||||||
|
import './ssr-polyfills'
|
||||||
|
|
||||||
|
import * as express from 'express'
|
||||||
|
import { join } from 'path'
|
||||||
|
|
||||||
|
// Faster server renders w/ Prod mode (dev mode never needed)
|
||||||
|
enableProdMode();
|
||||||
|
|
||||||
|
// Express server
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const PORT = process.env.PORT || 4000;
|
||||||
|
const DIST_FOLDER = join(process.cwd(), 'build');
|
||||||
|
|
||||||
|
import { AppServerModule } from './app.server.module'
|
||||||
|
|
||||||
|
app.engine('html', ngExpressEngine({
|
||||||
|
bootstrap: AppServerModule,
|
||||||
|
}));
|
||||||
|
|
||||||
|
app.set('view engine', 'html');
|
||||||
|
app.set('views', DIST_FOLDER);
|
||||||
|
|
||||||
|
// Example Express Rest API endpoints
|
||||||
|
// app.get('/api/**', (req, res) => { });
|
||||||
|
|
||||||
|
// Server static files from /browser
|
||||||
|
app.use('/static', express.static(DIST_FOLDER, {
|
||||||
|
maxAge: '1y'
|
||||||
|
}));
|
||||||
|
|
||||||
|
var proxy = require('express-http-proxy');
|
||||||
|
|
||||||
|
app.get(['/', '/login'], (req, res) => {
|
||||||
|
res.render('index', { req });
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.use('/', proxy('http://tabby.local:8000/api/', {
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Start up the Node server
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Node Express server listening on http://localhost:${PORT}`);
|
||||||
|
});
|
||||||
@@ -8,22 +8,20 @@ import { Config, Gateway, Version } from '../api'
|
|||||||
|
|
||||||
export class SocketProxy {
|
export class SocketProxy {
|
||||||
connect$ = new Subject<void>()
|
connect$ = new Subject<void>()
|
||||||
data$ = new Subject<Buffer>()
|
data$ = new Subject<Uint8Array>()
|
||||||
error$ = new Subject<Buffer>()
|
error$ = new Subject<Error>()
|
||||||
close$ = new Subject<Buffer>()
|
close$ = new Subject<void>()
|
||||||
|
|
||||||
url: string
|
url: string
|
||||||
authToken: string
|
authToken: string
|
||||||
webSocket: WebSocket|null
|
webSocket: WebSocket|null
|
||||||
initialBuffer: Buffer
|
initialBuffers: any[] = []
|
||||||
options: {
|
options: {
|
||||||
host: string
|
host: string
|
||||||
port: number
|
port: number
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor (private appConnector: AppConnectorService) {
|
constructor (private appConnector: AppConnectorService) { }
|
||||||
this.initialBuffer = Buffer.from('')
|
|
||||||
}
|
|
||||||
|
|
||||||
async connect (options) {
|
async connect (options) {
|
||||||
this.options = options
|
this.options = options
|
||||||
@@ -77,8 +75,10 @@ export class SocketProxy {
|
|||||||
} else if (msg._ === 'connected') {
|
} else if (msg._ === 'connected') {
|
||||||
this.connect$.next()
|
this.connect$.next()
|
||||||
this.connect$.complete()
|
this.connect$.complete()
|
||||||
this.webSocket.send(this.initialBuffer)
|
for (const b of this.initialBuffers) {
|
||||||
this.initialBuffer = Buffer.from('')
|
this.webSocket.send(b)
|
||||||
|
}
|
||||||
|
this.initialBuffers = []
|
||||||
} else if (msg._ === 'error') {
|
} else if (msg._ === 'error') {
|
||||||
console.error('Connection gateway error', msg)
|
console.error('Connection gateway error', msg)
|
||||||
this.close(new Error(msg.details))
|
this.close(new Error(msg.details))
|
||||||
@@ -93,7 +93,7 @@ export class SocketProxy {
|
|||||||
|
|
||||||
write (chunk: Buffer): void {
|
write (chunk: Buffer): void {
|
||||||
if (!this.webSocket?.readyState) {
|
if (!this.webSocket?.readyState) {
|
||||||
this.initialBuffer = Buffer.concat([this.initialBuffer, chunk])
|
this.initialBuffers.push(chunk)
|
||||||
} else {
|
} else {
|
||||||
this.webSocket.send(chunk)
|
this.webSocket.send(chunk)
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/ssr-interceptor.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* This interceptor ensures that the app makes requests
|
||||||
|
* with relative paths correctly server-side.
|
||||||
|
* Requests which start with a dot (ex. ./assets/...)
|
||||||
|
* or relative ones ( ex. /assets/...) will be converted
|
||||||
|
* to absolute paths
|
||||||
|
*/
|
||||||
|
import { Inject, Injectable, Injector, PLATFORM_ID } from '@angular/core';
|
||||||
|
import { isPlatformServer } from '@angular/common';
|
||||||
|
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
||||||
|
import { REQUEST } from '@nguniversal/express-engine/tokens';
|
||||||
|
|
||||||
|
import { Request } from 'express';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UniversalInterceptor implements HttpInterceptor {
|
||||||
|
constructor(
|
||||||
|
private readonly injector: Injector,
|
||||||
|
@Inject(PLATFORM_ID) private readonly platformId: any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
|
const isServer = isPlatformServer(this.platformId);
|
||||||
|
|
||||||
|
if (isServer && !request.url.startsWith('//') && (request.url.startsWith('./') || request.url.startsWith('/'))) {
|
||||||
|
const serverRequest = this.injector.get(REQUEST) as Request;
|
||||||
|
console.log(serverRequest)
|
||||||
|
const baseUrl = `${serverRequest.protocol}://${serverRequest.get('Host')}`;
|
||||||
|
let endpoint = request.url;
|
||||||
|
/**
|
||||||
|
* ISSUE https://github.com/angular/angular/issues/19224
|
||||||
|
* HttpClient doesn't support relative requests server-side
|
||||||
|
*/
|
||||||
|
if (endpoint.startsWith('.')) {
|
||||||
|
endpoint = endpoint.substring(1);
|
||||||
|
}
|
||||||
|
// Now the endpoint starts with '/'
|
||||||
|
request = request.clone({
|
||||||
|
url: `${baseUrl}${endpoint}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return next.handle(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/ssr-polyfills.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import * as domino from 'domino';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
const template = fs.readFileSync(path.join(process.cwd(), 'build-server', 'index.html')).toString();
|
||||||
|
const win = domino.createWindow(template);
|
||||||
|
|
||||||
|
global['window'] = win;
|
||||||
|
|
||||||
|
Object.defineProperty(win.document.body.style, 'transform', {
|
||||||
|
value: () => {
|
||||||
|
return {
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(win.document.body.style, 'z-index', {
|
||||||
|
value: () => {
|
||||||
|
return {
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
global['document'] = win.document;
|
||||||
|
global['CSS'] = null;
|
||||||
|
// global['atob'] = win.atob;
|
||||||
|
global['atob'] = (base64: string) => {
|
||||||
|
return Buffer.from(base64, 'base64').toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
function setDomTypes() {
|
||||||
|
// Make all Domino types available as types in the global env.
|
||||||
|
Object.assign(global, domino['impl']);
|
||||||
|
(global as any)['KeyboardEvent'] = domino['impl'].Event;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDomTypes();
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html class="tabby">
|
<html class="tabby">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="utf-8" />
|
||||||
<style id="custom-css"></style>
|
<style id="custom-css"></style>
|
||||||
<style>body { transition: 0.5s background; }</style>
|
<style>body { transition: 0.5s background; }</style>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": "src/",
|
"baseUrl": "src/",
|
||||||
"module": "esNext",
|
"module": "es2015",
|
||||||
"target": "es6",
|
"target": "es6",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
@@ -27,5 +27,5 @@
|
|||||||
"*": ["src/*"]
|
"*": ["src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"exclude": ["app-dist"]
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"baseUrl": "src/",
|
|
||||||
"module": "esNext",
|
|
||||||
"target": "es6",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"noImplicitAny": false,
|
|
||||||
"removeComments": false,
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"noImplicitReturns": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"declaration": true,
|
|
||||||
"lib": [
|
|
||||||
"dom",
|
|
||||||
"es5",
|
|
||||||
"es6",
|
|
||||||
"es7"
|
|
||||||
],
|
|
||||||
"paths": {
|
|
||||||
"*": ["src/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"exclude": ["app-dist"]
|
|
||||||
}
|
|
||||||
@@ -2,32 +2,24 @@ const path = require('path')
|
|||||||
const webpack = require('webpack')
|
const webpack = require('webpack')
|
||||||
const { AngularWebpackPlugin } = require('@ngtools/webpack')
|
const { AngularWebpackPlugin } = require('@ngtools/webpack')
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||||
|
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
|
||||||
|
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
||||||
|
|
||||||
|
|
||||||
const htmlPluginOptions = {
|
const htmlPluginOptions = {
|
||||||
hash: true,
|
hash: true,
|
||||||
minify: false
|
minify: false
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
const baseConfig = {
|
||||||
target: 'web',
|
|
||||||
entry: {
|
|
||||||
index: path.resolve(__dirname, 'src/index.ts'),
|
|
||||||
terminal: path.resolve(__dirname, 'src/terminal.ts'),
|
|
||||||
},
|
|
||||||
mode: process.env.DEV ? 'development' : 'production',
|
mode: process.env.DEV ? 'development' : 'production',
|
||||||
context: __dirname,
|
context: __dirname,
|
||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
output: {
|
|
||||||
path: path.join(__dirname, 'build'),
|
|
||||||
pathinfo: true,
|
|
||||||
publicPath: '/static/',
|
|
||||||
filename: '[name].js',
|
|
||||||
chunkFilename: '[name].bundle.js',
|
|
||||||
},
|
|
||||||
cache: !process.env.DEV ? false : {
|
cache: !process.env.DEV ? false : {
|
||||||
type: 'filesystem',
|
type: 'filesystem',
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
mainFields: ['esm2015', 'browser', 'module', 'main'],
|
||||||
modules: [
|
modules: [
|
||||||
'src/',
|
'src/',
|
||||||
'node_modules/',
|
'node_modules/',
|
||||||
@@ -53,14 +45,14 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.scss$/,
|
test: /\.scss$/,
|
||||||
use: ['style-loader', 'css-loader', 'sass-loader'],
|
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
|
||||||
exclude: /component\.scss/
|
exclude: /component\.scss/
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(ttf|eot|otf|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
test: /\.(ttf|eot|otf|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||||
type: 'asset/resource',
|
type: 'asset/resource',
|
||||||
},
|
},
|
||||||
{ test: /\.css$/, use: ['css-loader', 'sass-loader'] },
|
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
|
||||||
{
|
{
|
||||||
test: /\.(jpeg|png|svg)?$/,
|
test: /\.(jpeg|png|svg)?$/,
|
||||||
type: 'asset/resource',
|
type: 'asset/resource',
|
||||||
@@ -71,12 +63,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
new AngularWebpackPlugin({
|
new MiniCssExtractPlugin(),
|
||||||
tsconfig: 'tsconfig.main.json',
|
|
||||||
directTemplateLoading: false,
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
template: './src/index.html',
|
template: './src/index.html',
|
||||||
filename: 'index.html',
|
filename: 'index.html',
|
||||||
@@ -91,3 +79,62 @@ module.exports = {
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
name: 'browser',
|
||||||
|
target: 'web',
|
||||||
|
...baseConfig,
|
||||||
|
entry: {
|
||||||
|
index: path.resolve(__dirname, 'src/index.ts'),
|
||||||
|
terminal: path.resolve(__dirname, 'src/terminal.ts'),
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
...baseConfig.plugins,
|
||||||
|
new AngularWebpackPlugin({
|
||||||
|
tsconfig: 'tsconfig.json',
|
||||||
|
directTemplateLoading: false,
|
||||||
|
skipCodeGeneration: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
output: {
|
||||||
|
path: path.join(__dirname, 'build'),
|
||||||
|
pathinfo: true,
|
||||||
|
publicPath: '/static/',
|
||||||
|
filename: '[name].js',
|
||||||
|
chunkFilename: '[name].bundle.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'server',
|
||||||
|
target: 'node',
|
||||||
|
...baseConfig,
|
||||||
|
entry: {
|
||||||
|
'index.server': path.resolve(__dirname, 'src/index.server.ts'),
|
||||||
|
'server': path.resolve(__dirname, 'src/server.ts'),
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
...baseConfig.plugins,
|
||||||
|
new AngularWebpackPlugin({
|
||||||
|
entryModule: path.resolve(__dirname, 'src/app/app.server.module`#AppServerModule'),
|
||||||
|
mainPath: path.resolve(__dirname, 'src/index.server.ts'),
|
||||||
|
tsconfig: 'tsconfig.json',
|
||||||
|
directTemplateLoading: false,
|
||||||
|
platform: 1,
|
||||||
|
skipCodeGeneration: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
output: {
|
||||||
|
libraryTarget: 'commonjs',
|
||||||
|
path: path.join(__dirname, 'build-server'),
|
||||||
|
pathinfo: true,
|
||||||
|
publicPath: '/static/',
|
||||||
|
filename: '[name].js',
|
||||||
|
chunkFilename: '[name].bundle.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
if (process.env.BUNDLE_ANALYZER) {
|
||||||
|
module.exports[0].plugins.push(new BundleAnalyzerPlugin())
|
||||||
|
}
|
||||||
|
|||||||