This commit is contained in:
Eugene Pankov 2021-06-14 23:00:05 +02:00
parent dc69574c07
commit e1425c6c80
No known key found for this signature in database
GPG Key ID: 5896FCBBDD1CF4F4
32 changed files with 1502 additions and 422 deletions

3
.gitignore vendored
View File

@ -1,5 +1,4 @@
node_modules
index.d.ts
*.ignore.js
*.ignore.js.map
dist
@ -9,3 +8,5 @@ app-dist
db.sqlite3
__pycache__
.vscode
build
*.d.ts

View File

@ -1 +0,0 @@
<!DOCTYPE html><html><head><base href="dist/"><meta name="viewport" content="initial-scale=1, minimal-ui, shrink-to-fit=no"><link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400" rel="stylesheet"><script src="index.js" defer></script><title>Terminus</title></head><body><div class="terminal"><iframe src="terminal.html"></iframe></div></body></html>

View File

@ -1 +0,0 @@
import './styles.scss'

View File

@ -1,5 +1,5 @@
{
"name": "terminus-web",
"name": "terminus-web-container",
"version": "1.0.0",
"main": "index.js",
"scripts": {
@ -8,24 +8,40 @@
},
"private": true,
"devDependencies": {
"@angular/animations": "^11.0.0",
"@angular/common": "^11.0.0",
"@angular/compiler": "^11.0.0",
"@angular/compiler-cli": "^11.0.0",
"@angular/core": "^11.0.0",
"@angular/forms": "^11.0.0",
"@angular/platform-browser": "^11.0.0",
"@angular/platform-browser-dynamic": "^11.0.0",
"@fortawesome/fontawesome-free": "^5.7.2",
"@ng-bootstrap/ng-bootstrap": "11.0.0-beta.1",
"@ngtools/webpack": "^12.0.4",
"@terminus-term/to-string-loader": "^1.1.7-beta.1",
"@types/node": "^11.9.5",
"apply-loader": "^2.0.0",
"assert": "1.5.0",
"awesome-typescript-loader": "^5.2.1",
"bootstrap": "^4.3.1",
"bootstrap": "^5.0.1",
"browserify-zlib": "^0.2.0",
"buffer": "^6.0.3",
"constants-browserify": "^1.0.0",
"core-js": "^3.8.3",
"core-js": "^3.14.0",
"crypto-browserify": "^3.12.0",
"css-loader": "^2.1.0",
"deepmerge": "^4.2.2",
"file-loader": "^1.1.11",
"js-yaml": "^4.1.0",
"ngx-toastr": "^14.0.0",
"node-sass": "^6.0.0",
"pug": "^2.0.3",
"pug": "^3.0.2",
"pug-cli": "^1.0.0-alpha6",
"pug-html-loader": "^1.1.5",
"raw-loader": "^1.0.0",
"pug-loader": "^2.4.0",
"raw-loader": "^4.0.2",
"rxjs": "^7.1.0",
"sass-loader": "^11.1.1",
"script-loader": "^0.7.2",
"setimmediate": "^1.0.5",
@ -35,10 +51,11 @@
"stream-browserify": "^3.0.0",
"string_decoder": "^1.3.0",
"style-loader": "^0.23.1",
"typescript": "3.3.3333",
"val-loader": "^1.1.1",
"webpack": "^5.37.0",
"webpack-cli": "^4.7.0"
"typescript": "~4.1",
"val-loader": "^4.0.0",
"webpack": "^5.38.1",
"webpack-cli": "^4.7.2",
"zone.js": "^0.11.4"
},
"resolutions": {
"**/util": "^0.12.0"

4
polyfills.d.ts vendored
View File

@ -1,4 +0,0 @@
export declare function utf8Write(string: any, offset: any, length: any): any;
export declare function base64Slice(start: any, end: any): any;
export declare function latin1Slice(start: any, end: any): string;
export declare function utf8Slice(this: any, start: any, end: any): any;

21
src/app.module.ts Normal file
View File

@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http'
import { AppComponent } from './components/app.component'
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
HttpClientXsrfModule,
NgbDropdownModule,
],
declarations: [
AppComponent,
],
bootstrap: [AppComponent]
})
export class AppModule { }

1
src/assets/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" x="0" y="0" version="1.1" viewBox="0 0 1024 1024" xml:space="preserve" style="enable-background:new 0 0 1024 1024"><style type="text/css">.st0{fill:url(#SVGID_1_)}.st1{opacity:.16;fill:url(#SVGID_2_)}.st2{fill:url(#SVGID_3_)}.st3{opacity:.16;fill:url(#SVGID_4_)}.st4{fill:url(#SVGID_5_)}.st5{opacity:.15;fill:url(#SVGID_6_)}.st6{fill:url(#SVGID_7_)}</style><g><linearGradient id="SVGID_1_" x1="260.967" x2="919.184" y1="871.181" y2="491.16" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#669abd"/><stop offset="1" style="stop-color:#77dbdb"/></linearGradient><polygon points="297.54 934.52 882.6 596.72 882.61 427.82 297.54 765.65" class="st0"/><linearGradient id="SVGID_2_" x1="553.505" x2="626.647" y1="617.828" y2="744.513" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="297.54 934.52 882.6 596.72 882.61 427.82 297.54 765.65" class="st1"/></g><g><linearGradient id="SVGID_3_" x1="114.663" x2="334.091" y1="744.528" y2="871.214" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#6a8fad"/><stop offset="1" style="stop-color:#669abd"/></linearGradient><polygon points="151.23 681.18 151.22 850.09 297.54 934.52 297.54 765.65" class="st2"/><linearGradient id="SVGID_4_" x1="260.948" x2="187.806" y1="744.528" y2="871.213" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="151.23 681.18 151.22 850.09 297.54 934.52 297.54 765.65" class="st3"/></g><g><linearGradient id="SVGID_5_" x1="114.663" x2="553.503" y1="237.793" y2="491.157" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#6a8fad"/><stop offset="1" style="stop-color:#669abd"/></linearGradient><polygon points="151.23 174.45 151.21 343.36 443.79 512.27 590.08 427.81" class="st4"/><linearGradient id="SVGID_6_" x1="370.656" x2="297.509" y1="301.128" y2="427.822" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="151.23 174.45 151.21 343.36 443.79 512.27 590.08 427.81" class="st5"/></g><linearGradient id="SVGID_7_" x1="78.091" x2="736.337" y1="554.498" y2="174.459" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#ccecff"/><stop offset="1" style="stop-color:#9feced"/></linearGradient><polygon points="297.51 765.64 151.23 681.18 590.08 427.81 151.23 174.45 297.5 90 882.61 427.82" class="st6"/></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,15 @@
.sidebar
img.logo(src='{{_logo}}')
div(ngbDropdown)
button.btn.btn-secondary(ngbDropdownToggle) Cfg
//) !{require('../icons/download-solid.svg')}
div(ngbDropdownMenu)
a(
*ngFor='let config of configs',
ngbDropdownItem,
(click)='selectConfig(config)'
) Config modified at {{config.modified_at}}
.terminal([hidden]='!showApp')
iframe(#iframe)

View File

@ -0,0 +1,34 @@
:host {
position: absolute;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
display: flex;
}
.sidebar {
width: 64px;
flex: none;
.logo {
width: 64px;
height: 64px;
}
}
.terminal {
flex: 1 1 0;
overflow: hidden;
position: relative;
}
iframe {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border: none;
}

View File

@ -0,0 +1,50 @@
import { Component, ElementRef, ViewChild } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { AppConnectorService } from '../services/appConnector.service'
@Component({
selector: 'app',
templateUrl: './app.component.pug',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
_logo = require('../assets/logo.svg')
showApp = false
configs: any[] = []
@ViewChild('iframe') iframe: ElementRef
constructor (
private appConnector: AppConnectorService,
private http: HttpClient,
) {
window.addEventListener('message', event => {
if (event.data === 'request-connector') {
this.iframe.nativeElement.contentWindow['__connector__'] = this.appConnector
this.iframe.nativeElement.contentWindow.postMessage('connector-ready', '*')
}
})
}
async ngAfterViewInit () {
this.configs = await this.http.get('/api/1/configs').toPromise()
this.selectConfig(this.configs[0])
}
unloadApp () {
this.showApp = false
this.iframe.nativeElement.src = 'about:blank'
}
loadApp () {
this.iframe.nativeElement.src = '/terminal'
this.showApp = true
}
selectConfig (config: any) {
this.appConnector.config = config
this.unloadApp()
setTimeout(() => {
this.loadApp()
})
}
}

View File

@ -1,11 +1,9 @@
doctype html
html
head
base(href='dist/')
meta(name='viewport', content='initial-scale=1, minimal-ui, shrink-to-fit=no')
link(href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400", rel="stylesheet")
script(src='index.js', defer)
script(src='/build/index.js', defer)
title Terminus
body
.terminal
iframe(src='terminal.html')
app

15
src/index.ts Normal file
View File

@ -0,0 +1,15 @@
import 'zone.js'
import 'core-js/proposals/reflect-metadata'
import 'core-js/features/array/flat'
import 'rxjs'
import { enableProdMode } from '@angular/core'
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
import './styles.scss'
import { AppModule } from './app.module'
enableProdMode()
platformBrowserDynamic().bootstrapModule(AppModule)

View File

@ -0,0 +1,24 @@
import { Subject } from 'rxjs'
import { debounceTime } from 'rxjs/operators'
import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core'
@Injectable({ providedIn: 'root' })
export class AppConnectorService {
config: any
private configUpdate = new Subject<string>()
constructor (private http: HttpClient) {
this.configUpdate.pipe(debounceTime(1000)).subscribe(content => {
this.http.patch(`/api/1/configs/${this.config.id}`, { content }).toPromise()
})
}
async loadConfig (): Promise<string> {
return this.config.content
}
async saveConfig (content: string): Promise<void> {
this.configUpdate.next(content)
}
}

9
src/styles.scss Normal file
View File

@ -0,0 +1,9 @@
$font-family-sans-serif: "Source Sans Pro";
$border-radius-lg: 0;
$btn-border-width: 3px;
body {
overscroll-behavior: none;
}
@import "node_modules/bootstrap/scss/bootstrap";

0
src/terminal-styles.scss Normal file
View File

View File

@ -2,7 +2,7 @@ doctype html
html.terminus
head
meta(charset='UTF-8')
script(src='/dist/terminal.js')
script(src='/build/terminal.js')
style#custom-css
style.
body { transition: 0.5s background; }

View File

@ -15,6 +15,17 @@ Object.assign(window, {
})
import * as angularCoreModule from '@angular/core'
import * as angularCompilerModule from '@angular/compiler'
import * as angularCommonModule from '@angular/common'
import * as angularFormsModule from '@angular/forms'
import * as angularPlatformBrowserModule from '@angular/platform-browser'
import * as angularPlatformBrowserAnimationsModule from '@angular/platform-browser/animations'
import * as angularPlatformBrowserDynamicModule from '@angular/platform-browser-dynamic'
import * as angularAnimationsModule from '@angular/animations'
import * as ngBootstrapModule from '@ng-bootstrap/ng-bootstrap'
import * as ngxToastrModule from 'ngx-toastr'
import 'core-js/proposals/reflect-metadata'
import '@fortawesome/fontawesome-free/css/solid.css'
@ -22,7 +33,6 @@ import '@fortawesome/fontawesome-free/css/brands.css'
import '@fortawesome/fontawesome-free/css/fontawesome.css'
import 'source-code-pro/source-code-pro.css'
import 'source-sans-pro/source-sans-pro.css'
import * as yaml from 'js-yaml'
import { Duplex } from 'stream-browserify'
import { Buffer } from 'buffer'
@ -88,21 +98,6 @@ async function start () {
}
}
let windowReadyCallback
const configContent = `
enableAnalytics: false
enableWelcomeTab: false
terminal:
font: "Source Code Pro"
autoOpen: true
rightClick: menu
copyOnSelect: false
appearance:
vibrancy: false
pluginBlacklist: []
`
const config = yaml.load(configContent)
const mocks = {
fs: {
realpathSync: path => {
@ -110,9 +105,6 @@ async function start () {
return path
},
existsSync: path => {
if (path === 'app-path/config.yaml') {
return true
}
console.warn('mock existsSync', path)
return false
},
@ -126,9 +118,6 @@ async function start () {
},
writeFileSync: () => null,
readFileSync: (path) => {
if (path === 'app-path/config.yaml') {
return configContent
}
return ''
},
readFile: (path, enc, cb) => {
@ -186,19 +175,13 @@ async function start () {
},
electron: {
ipcRenderer: {
on: () => null,
on: (e, c) => {
console.log('[ipc listen]', e)
},
once: (e, c) => {
if (e === 'start') {
windowReadyCallback = c
}
console.log('[ipc listen once]', e)
},
send: msg => {
if (msg === 'ready') {
windowReadyCallback(null, {
config,
executable: '---',
})
}
console.log('[ipc]', msg)
}
},
@ -277,7 +260,7 @@ async function start () {
exists: path => mocks.fs.existsSync(path),
existsSync: path => mocks.fs.existsSync(path),
},
constants: {},
constants: require('constants-browserify'),
'hterm-umdjs': {
hterm: {
PreferenceManager: class { set () {} },
@ -307,16 +290,16 @@ async function start () {
;(mocks.assert as any).notStrictEqual = () => true
let builtins = {
'@angular/core': require('@angular/core'),
'@angular/compiler': require('@angular/compiler'),
'@angular/common': require('@angular/common'),
'@angular/forms': require('@angular/forms'),
'@angular/platform-browser': require('@angular/platform-browser'),
'@angular/platform-browser/animations': require('@angular/platform-browser/animations'),
'@angular/platform-browser-dynamic': require('@angular/platform-browser-dynamic'),
'@angular/animations': require('@angular/animations'),
'@ng-bootstrap/ng-bootstrap': require('@ng-bootstrap/ng-bootstrap'),
'ngx-toastr': require('ngx-toastr'),
'@angular/core': angularCoreModule,
'@angular/compiler': angularCompilerModule,
'@angular/common': angularCommonModule,
'@angular/forms': angularFormsModule,
'@angular/platform-browser': angularPlatformBrowserModule,
'@angular/platform-browser/animations': angularPlatformBrowserAnimationsModule,
'@angular/platform-browser-dynamic': angularPlatformBrowserDynamicModule,
'@angular/animations': angularAnimationsModule,
'@ng-bootstrap/ng-bootstrap': ngBootstrapModule,
'ngx-toastr': ngxToastrModule,
'deepmerge': require('deepmerge'),
'rxjs': require('rxjs'),
'rxjs/operators': require('rxjs/operators'),
@ -339,18 +322,17 @@ async function start () {
window['require'].main = {
paths: []
}
} as any
window['module'] = {
paths: []
}
} as any
window['require'].resolve = path => null
window['require'].resolve = (path => null) as any
window['Buffer'] = mocks.buffer.Buffer
window['__dirname'] = '__dirname'
window['setImmediate'] = setTimeout
window['setImmediate'] = setTimeout as any
mocks.module['prototype'] = { require: window['require'] }
window['terminusConfig'] = configContent
Buffer.prototype['latin1Slice'] = latin1Slice
Buffer.prototype['utf8Slice'] = utf8Slice
@ -364,7 +346,7 @@ async function start () {
async function loadPlugin (name, file = 'index.js') {
const url = `../app-dist/${name}/dist/${file}`
const e = document.createElement('script')
window['module'] = { exports: {} }
window['module'] = { exports: {} } as any
window['exports'] = window['module'].exports
await new Promise(resolve => {
e.onload = resolve
@ -392,6 +374,17 @@ async function start () {
await loadPlugin('app', 'preload.js')
await loadPlugin('app', 'bundle-web.js')
document.querySelector('app-root')['style'].display = 'flex'
await new Promise<void>(resolve => {
window.addEventListener('message', event => {
if (event.data === 'connector-ready') {
resolve()
}
})
window.parent.postMessage('request-connector', '*')
})
const config = window['__connector__'].loadConfig()
window['bootstrapTerminus'](pluginModules, { config })
}

View File

@ -1,27 +0,0 @@
$font-family-sans-serif: "Source Sans Pro";
$border-radius-lg: 0;
$btn-border-width: 3px;
// @import "node_modules/bootstrap/scss/bootstrap";
.terminal {
position: absolute;
top: 100px;
left: 100px;
width: calc(100vw - 200px);
height: calc(100vh - 200px);
border-radius: 9px;
overflow: hidden;
box-shadow: 0 0 100px black;
}
iframe {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border: none;
}

20
terminal.d.ts vendored
View File

@ -1,20 +0,0 @@
/// <reference types="node" />
import 'core-js/proposals/reflect-metadata';
import '@fortawesome/fontawesome-free/css/solid.css';
import '@fortawesome/fontawesome-free/css/brands.css';
import '@fortawesome/fontawesome-free/css/fontawesome.css';
import 'source-code-pro/source-code-pro.css';
import 'source-sans-pro/source-sans-pro.css';
import { Duplex } from 'stream-browserify';
import './terminal-styles.scss';
export declare class Socket extends Duplex {
webSocket: WebSocket;
initialBuffer: Buffer;
constructor();
connect(): void;
setNoDelay(): void;
setTimeout(): void;
_read(size: number): void;
_write(chunk: Buffer, _encoding: string, callback: (error?: Error | null) => void): void;
_destroy(error: Error | null, callback: (error: Error | null) => void): void;
}

19
terminus/app/api.py Normal file
View File

@ -0,0 +1,19 @@
# from rest_framework import fields
from rest_framework.viewsets import ModelViewSet
from rest_framework.serializers import ModelSerializer
from .models import Config
class ConfigSerializer(ModelSerializer):
class Meta:
model = Config
fields = '__all__'
class ConfigViewSet(ModelViewSet):
queryset = Config.objects.all()
serializer_class = ConfigSerializer
def get_queryset(self):
return Config.objects.filter(user=self.request.user)

View File

@ -2,6 +2,7 @@ from django.db import models
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver
from django.db.models.signals import post_save
class Config(models.Model):
@ -19,4 +20,7 @@ class User(AbstractUser):
modified_at = models.DateTimeField(auto_now=True)
# @receiver
# @receiver(user_logged_in)
# def post_login(sender, user, request, **kwargs):
# if not user.active_config:
# user.active_config = Config.objects.filter()

View File

@ -1,16 +1,19 @@
from django.urls import path, include
from rest_framework import routers
from . import api
from . import consumers
from . import views
router = routers.DefaultRouter(trailing_slash=False)
# router.register('api/2/auth/saml', SAMLProviderViewSet)
router.register('api/1/configs', api.ConfigViewSet)
urlpatterns = [
path('', views.IndexView.as_view()),
path('terminal', views.TerminalView.as_view()),
path('app-dist/<path:path>', views.AppDistView.as_view()),
path('dist/<path:path>', views.DistView.as_view()),
path('build/<path:path>', views.BuildView.as_view()),
path('', include(router.urls)),
]

View File

@ -7,7 +7,14 @@ from django.views import static
class IndexView(APIView):
def get(self, request, format=None):
return static.serve(request, 'terminal.html', document_root=str(settings.BASE_DIR / 'dist'))
return static.serve(request, 'index.html', document_root=str(settings.BASE_DIR / 'build'))
class TerminalView(APIView):
def get(self, request, format=None):
response = static.serve(request, 'terminal.html', document_root=str(settings.BASE_DIR / 'build'))
response['X-Frame-Options'] = 'SAMEORIGIN'
return response
class AppDistView(APIView):
@ -15,6 +22,6 @@ class AppDistView(APIView):
return static.serve(request, path, document_root=str(settings.BASE_DIR / 'app-dist'))
class DistView(APIView):
class BuildView(APIView):
def get(self, request, path=None, format=None):
return static.serve(request, path, document_root=str(settings.BASE_DIR / 'dist'))
return static.serve(request, path, document_root=str(settings.BASE_DIR / 'build'))

View File

@ -128,3 +128,8 @@ STATIC_URL = '/static/'
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
CSRF_USE_SESSIONS = False
CSRF_COOKIE_HTTPONLY = False
CSRF_COOKIE_NAME = 'XSRF-TOKEN'
CSRF_HEADER_NAME = 'HTTP_X_XSRF_TOKEN'

View File

@ -1,5 +1,6 @@
{
"compilerOptions": {
"baseUrl": "src/",
"module": "esNext",
"target": "es6",
"moduleResolution": "node",
@ -21,7 +22,10 @@
"es5",
"es6",
"es7"
]
],
"paths": {
"*": ["src/*"]
}
},
"exclude": ["app-dist"]
}

31
tsconfig.main.json Normal file
View File

@ -0,0 +1,31 @@
{
"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"]
}

View File

@ -1,120 +1,4 @@
const path = require('path')
const webpack = require('webpack')
module.exports = {
target: 'web',
entry: {
'index.ignore': 'file-loader?name=../index.html!pug-html-loader!' + path.resolve(__dirname, './index.pug'),
'terminal.ignore': 'file-loader?name=terminal.html!pug-html-loader!' + path.resolve(__dirname, './terminal.pug'),
index: path.resolve(__dirname, 'index.ts'),
terminal: path.resolve(__dirname, 'terminal.ts'),
},
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
context: __dirname,
devtool: 'cheap-module-source-map',
output: {
path: path.join(__dirname, 'dist'),
pathinfo: true,
filename: '[name].js',
chunkFilename: '[name].bundle.js',
},
resolve: {
modules: [
...[
'../terminus/terminus-core/node_modules/',
'../terminus/terminus-settings/node_modules/',
'../terminus/terminus-terminal/node_modules/',
'../terminus/node_modules',
'../terminus/app/node_modules',
'../terminus/app/assets/',
].map(x => path.join(__dirname, x)),
'node_modules/',
],
extensions: ['.ts', '.js'],
fallback: {
stream: require.resolve('stream-browserify'),
assert: require.resolve('assert/'),
constants: require.resolve('constants-browserify'),
util: require.resolve('util/'),
},
},
externals: {
'dns': 'commonjs dns',
'tls': 'commonjs tls',
'tty': 'commonjs tty',
'crypto': 'commonjs crypto',
'querystring': 'commonjs querystring',
'https': 'commonjs https',
'http': 'commonjs http',
'url': 'commonjs url',
'zlib': 'commonjs zlib',
'../build/Release/cpufeatures.node': 'commonjs ../build/Release/cpufeatures.node',
'./crypto/build/Release/sshcrypto.node': 'commonjs ./crypto/build/Release/sshcrypto.node',
'terminus-core': 'commonjs terminus-core',
'terminus-terminal': 'commonjs terminus-terminal',
'terminus-settings': 'commonjs terminus-settings',
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
},
},
},
{ test: /terminus\/app\/dist/, use: ['script-loader'] },
// { test: /dist\/index/, use: ['raw-loader'] },
{
test: /\.(ttf|eot|otf|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]',
},
},
},
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
{ test: /\.css$/, use: ['css-loader', 'sass-loader'] },
{
test: /\.(jpeg|png|svg)?$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
}
}
],
}
}
const externals = [
'@electron/remote',
'any-promise',
'child_process',
'electron-promise-ipc',
'electron',
'fontmanager-redux',
'fs',
'keytar',
'hterm-umdjs',
'macos-native-processlist',
'native-process-working-directory',
'net',
'os',
'path',
'readline',
'serialport',
'socksv5',
'windows-native-registry',
'windows-process-tree',
'windows-process-tree/build/Release/windows_process_tree.node',
module.exports = [
require('./webpack.main.config'),
require('./webpack.terminal.config'),
]
for (const k of externals) {
module.exports.externals[k] = `commonjs ${k}`
}

67
webpack.main.config.js Normal file
View File

@ -0,0 +1,67 @@
const path = require('path')
const webpack = require('webpack')
const { AngularWebpackPlugin } = require('@ngtools/webpack')
module.exports = {
target: 'web',
entry: {
'index.ignore': 'file-loader?name=index.html!pug-html-loader!' + path.resolve(__dirname, './src/index.pug'),
index: path.resolve(__dirname, 'src/index.ts'),
},
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
context: __dirname,
devtool: 'cheap-module-source-map',
output: {
path: path.join(__dirname, 'build'),
pathinfo: true,
filename: '[name].js',
chunkFilename: '[name].bundle.js',
},
resolve: {
modules: [
'src/',
'node_modules/',
],
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.[jt]sx?$/,
loader: '@ngtools/webpack',
},
{ test: /terminus\/app\/dist/, use: ['script-loader'] },
{
test: /\.pug$/,
use: ['apply-loader', 'pug-loader'],
include: /component\.pug/
},
{
test: /\.scss$/,
use: ['@terminus-term/to-string-loader', 'css-loader', 'sass-loader'],
include: /component\.scss/
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
exclude: /component\.scss/
},
{
test: /\.(ttf|eot|otf|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
type: 'asset/resource',
},
{ test: /\.css$/, use: ['css-loader', 'sass-loader'] },
{
test: /\.(jpeg|png|svg)?$/,
type: 'asset/resource',
}
],
},
plugins: [
new AngularWebpackPlugin({
tsconfig: 'tsconfig.main.json',
directTemplateLoading: false,
}),
],
}

122
webpack.terminal.config.js Normal file
View File

@ -0,0 +1,122 @@
const path = require('path')
const webpack = require('webpack')
module.exports = {
name: 'web-container-terminal',
target: 'web',
entry: {
'terminal.ignore': 'file-loader?name=terminal.html!pug-html-loader!' + path.resolve(__dirname, './src/terminal.pug'),
terminal: path.resolve(__dirname, 'src/terminal.ts'),
},
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
context: __dirname,
devtool: 'cheap-module-source-map',
output: {
path: path.join(__dirname, 'build'),
pathinfo: true,
filename: '[name].js',
chunkFilename: '[name].bundle.js',
},
resolve: {
modules: [
...[
'../terminus/terminus-core/node_modules/',
'../terminus/terminus-settings/node_modules/',
'../terminus/terminus-terminal/node_modules/',
'../terminus/node_modules',
'../terminus/app/node_modules',
'../terminus/app/assets/',
'src',
].map(x => path.join(__dirname, x)),
'node_modules/',
],
extensions: ['.ts', '.js'],
fallback: {
stream: require.resolve('stream-browserify'),
assert: require.resolve('assert/'),
constants: require.resolve('constants-browserify'),
util: require.resolve('util/'),
},
},
externals: {
'dns': 'commonjs dns',
'tls': 'commonjs tls',
'tty': 'commonjs tty',
'crypto': 'commonjs crypto',
'querystring': 'commonjs querystring',
'https': 'commonjs https',
'http': 'commonjs http',
'url': 'commonjs url',
'zlib': 'commonjs zlib',
'../build/Release/cpufeatures.node': 'commonjs ../build/Release/cpufeatures.node',
'./crypto/build/Release/sshcrypto.node': 'commonjs ./crypto/build/Release/sshcrypto.node',
'terminus-core': 'commonjs terminus-core',
'terminus-terminal': 'commonjs terminus-terminal',
'terminus-settings': 'commonjs terminus-settings',
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.container.json'),
},
},
},
// { test: /terminus\/app\/dist/, use: ['script-loader'] },
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
test: /\.(ttf|eot|otf|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]',
},
},
},
{ test: /\.css$/, use: ['css-loader', 'sass-loader'] },
{
test: /\.(jpeg|png|svg)?$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
}
}
],
}
}
const externals = [
'@electron/remote',
'any-promise',
'child_process',
'electron-promise-ipc',
'electron',
'fontmanager-redux',
'fs',
'keytar',
'hterm-umdjs',
'macos-native-processlist',
'native-process-working-directory',
'net',
'os',
'path',
'readline',
'serialport',
'socksv5',
'windows-native-registry',
'windows-process-tree',
'windows-process-tree/build/Release/windows_process_tree.node',
]
for (const k of externals) {
module.exports.externals[k] = `commonjs ${k}`
}

1167
yarn.lock

File diff suppressed because it is too large Load Diff