Merge pull request #1648 from Tyriar/webpack_demo

Improve demo build task
This commit is contained in:
Daniel Imms
2018-09-08 02:58:58 -07:00
committed by GitHub
19 changed files with 869 additions and 320 deletions
+74 -60
View File
@@ -1,4 +1,13 @@
import * as Terminal from '../build/xterm'; /**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*
* This file is the entry point for browserify.
*/
/// <reference path="../typings/xterm.d.ts"/>
import { Terminal } from '../lib/public/Terminal';
import * as attach from '../build/addons/attach/attach'; import * as attach from '../build/addons/attach/attach';
import * as fit from '../build/addons/fit/fit'; import * as fit from '../build/addons/fit/fit';
import * as fullscreen from '../build/addons/fullscreen/fullscreen'; import * as fullscreen from '../build/addons/fullscreen/fullscreen';
@@ -6,6 +15,14 @@ import * as search from '../build/addons/search/search';
import * as webLinks from '../build/addons/webLinks/webLinks'; import * as webLinks from '../build/addons/webLinks/webLinks';
import * as winptyCompat from '../build/addons/winptyCompat/winptyCompat'; import * as winptyCompat from '../build/addons/winptyCompat/winptyCompat';
// Pulling in the module's types relies on the <reference> above, it's looks a
// little weird here as we're importing "this" module
import { Terminal as TerminalType } from 'xterm';
export interface IWindowWithTerminal extends Window {
term: TerminalType;
}
declare let window: IWindowWithTerminal;
Terminal.applyAddon(attach); Terminal.applyAddon(attach);
Terminal.applyAddon(fit); Terminal.applyAddon(fit);
@@ -15,20 +32,20 @@ Terminal.applyAddon(webLinks);
Terminal.applyAddon(winptyCompat); Terminal.applyAddon(winptyCompat);
var term, let term;
protocol, let protocol;
socketURL, let socketURL;
socket, let socket;
pid; let pid;
var terminalContainer = document.getElementById('terminal-container'), const terminalContainer = document.getElementById('terminal-container');
actionElements = { const actionElements = {
findNext: document.querySelector('#find-next'), findNext: <HTMLInputElement>document.querySelector('#find-next'),
findPrevious: document.querySelector('#find-previous') findPrevious: <HTMLInputElement>document.querySelector('#find-previous')
}, };
paddingElement = document.getElementById('padding'); const paddingElement = <HTMLInputElement>document.getElementById('padding');
function setPadding() { function setPadding(): void {
term.element.style.padding = parseInt(paddingElement.value, 10).toString() + 'px'; term.element.style.padding = parseInt(paddingElement.value, 10).toString() + 'px';
term.fit(); term.fit();
} }
@@ -52,20 +69,20 @@ const disposeRecreateButtonHandler = () => {
document.getElementById('dispose').addEventListener('click', disposeRecreateButtonHandler); document.getElementById('dispose').addEventListener('click', disposeRecreateButtonHandler);
function createTerminal() { function createTerminal(): void {
// Clean terminal // Clean terminal
while (terminalContainer.children.length) { while (terminalContainer.children.length) {
terminalContainer.removeChild(terminalContainer.children[0]); terminalContainer.removeChild(terminalContainer.children[0]);
} }
term = new Terminal({}); term = new Terminal({});
window.term = term; // Expose `term` to window for debugging purposes window.term = term; // Expose `term` to window for debugging purposes
term.on('resize', function (size) { term.on('resize', (size: { cols: number, rows: number }) => {
if (!pid) { if (!pid) {
return; return;
} }
var cols = size.cols, const cols = size.cols;
rows = size.rows, const rows = size.rows;
url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows; const url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;
fetch(url, {method: 'POST'}); fetch(url, {method: 'POST'});
}); });
@@ -80,8 +97,8 @@ function createTerminal() {
addDomListener(paddingElement, 'change', setPadding); addDomListener(paddingElement, 'change', setPadding);
addDomListener(actionElements.findNext, 'keypress', function (e) { addDomListener(actionElements.findNext, 'keypress', (e) => {
if (e.key === "Enter") { if (e.key === 'Enter') {
e.preventDefault(); e.preventDefault();
let searchOptions = { let searchOptions = {
regex: document.getElementById('regex').checked, regex: document.getElementById('regex').checked,
@@ -91,8 +108,8 @@ function createTerminal() {
term.findNext(actionElements.findNext.value, searchOptions); term.findNext(actionElements.findNext.value, searchOptions);
} }
}); });
addDomListener(actionElements.findPrevious, 'keypress', function (e) { addDomListener(actionElements.findPrevious, 'keypress', (e) => {
if (e.key === "Enter") { if (e.key === 'Enter') {
e.preventDefault(); e.preventDefault();
let searchOptions = { let searchOptions = {
regex: document.getElementById('regex').checked, regex: document.getElementById('regex').checked,
@@ -104,18 +121,18 @@ function createTerminal() {
}); });
// fit is called within a setTimeout, cols and rows need this. // fit is called within a setTimeout, cols and rows need this.
setTimeout(function () { setTimeout(() => {
initOptions(term); initOptions(term);
document.getElementById(`opt-cols`).value = term.cols; // TODO: Clean this up, opt-cols/rows doesn't exist anymore
document.getElementById(`opt-rows`).value = term.rows; (<HTMLInputElement>document.getElementById(`opt-cols`)).value = term.cols;
paddingElement.value = 0; (<HTMLInputElement>document.getElementById(`opt-rows`)).value = term.rows;
paddingElement.value = '0';
// Set terminal size again to set the specific dimensions on the demo // Set terminal size again to set the specific dimensions on the demo
updateTerminalSize(); updateTerminalSize();
fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) { fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then((res) => {
res.text().then((processId) => {
res.text().then(function (processId) {
pid = processId; pid = processId;
socketURL += processId; socketURL += processId;
socket = new WebSocket(socketURL); socket = new WebSocket(socketURL);
@@ -127,22 +144,21 @@ function createTerminal() {
}, 0); }, 0);
} }
function runRealTerminal() { function runRealTerminal(): void {
term.attach(socket); term.attach(socket);
term._initialized = true; term._initialized = true;
} }
function runFakeTerminal() { // TODO: Maybe fake terminal should be removed? Not sure it's useful anymore
function runFakeTerminal(): void {
if (term._initialized) { if (term._initialized) {
return; return;
} }
term._initialized = true; term._initialized = true;
var shellprompt = '$ '; term.prompt = () => {
term.write('\r\n$ ');
term.prompt = function () {
term.write('\r\n' + shellprompt);
}; };
term.writeln('Welcome to xterm.js'); term.writeln('Welcome to xterm.js');
@@ -151,14 +167,12 @@ function runFakeTerminal() {
term.writeln(''); term.writeln('');
term.prompt(); term.prompt();
term._core.register(term.addDisposableListener('key', function (key, ev) { term._core.register(term.addDisposableListener('key', (key, ev) => {
var printable = ( const printable = !ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey;
!ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey
);
if (ev.keyCode == 13) { if (ev.keyCode === 13) {
term.prompt(); term.prompt();
} else if (ev.keyCode == 8) { } else if (ev.keyCode === 8) {
// Do not delete the prompt // Do not delete the prompt
if (term.x > 2) { if (term.x > 2) {
term.write('\b \b'); term.write('\b \b');
@@ -168,13 +182,13 @@ function runFakeTerminal() {
} }
})); }));
term._core.register(term.addDisposableListener('paste', function (data, ev) { term._core.register(term.addDisposableListener('paste', (data, ev) => {
term.write(data); term.write(data);
})); }));
} }
function initOptions(term) { function initOptions(term: TerminalType): void {
var blacklistedOptions = [ const blacklistedOptions = [
// Internal only options // Internal only options
'cancelEvents', 'cancelEvents',
'convertEol', 'convertEol',
@@ -186,7 +200,7 @@ function initOptions(term) {
// Complex option // Complex option
'theme' 'theme'
]; ];
var stringOptions = { const stringOptions = {
bellSound: null, bellSound: null,
bellStyle: ['none', 'sound'], bellStyle: ['none', 'sound'],
cursorStyle: ['block', 'underline', 'bar'], cursorStyle: ['block', 'underline', 'bar'],
@@ -196,9 +210,9 @@ function initOptions(term) {
fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
rendererType: ['dom', 'canvas'] rendererType: ['dom', 'canvas']
}; };
var options = Object.keys(term._core.options); const options = Object.keys((<any>term)._core.options);
var booleanOptions = []; const booleanOptions = [];
var numberOptions = []; const numberOptions = [];
options.filter(o => blacklistedOptions.indexOf(o) === -1).forEach(o => { options.filter(o => blacklistedOptions.indexOf(o) === -1).forEach(o => {
switch (typeof term.getOption(o)) { switch (typeof term.getOption(o)) {
case 'boolean': case 'boolean':
@@ -214,7 +228,7 @@ function initOptions(term) {
} }
}); });
var html = ''; let html = '';
html += '<div class="option-group">'; html += '<div class="option-group">';
booleanOptions.forEach(o => { booleanOptions.forEach(o => {
html += `<div class="option"><label><input id="opt-${o}" type="checkbox" ${term.getOption(o) ? 'checked' : ''}/> ${o}</label></div>`; html += `<div class="option"><label><input id="opt-${o}" type="checkbox" ${term.getOption(o) ? 'checked' : ''}/> ${o}</label></div>`;
@@ -228,24 +242,24 @@ function initOptions(term) {
if (stringOptions[o]) { if (stringOptions[o]) {
html += `<div class="option"><label>${o} <select id="opt-${o}">${stringOptions[o].map(v => `<option ${term.getOption(o) === v ? 'selected' : ''}>${v}</option>`).join('')}</select></label></div>`; html += `<div class="option"><label>${o} <select id="opt-${o}">${stringOptions[o].map(v => `<option ${term.getOption(o) === v ? 'selected' : ''}>${v}</option>`).join('')}</select></label></div>`;
} else { } else {
html += `<div class="option"><label>${o} <input id="opt-${o}" type="text" value="${term.getOption(o)}"/></label></div>` html += `<div class="option"><label>${o} <input id="opt-${o}" type="text" value="${term.getOption(o)}"/></label></div>`;
} }
}); });
html += '</div>'; html += '</div>';
var container = document.getElementById('options-container'); const container = document.getElementById('options-container');
container.innerHTML = html; container.innerHTML = html;
// Attach listeners // Attach listeners
booleanOptions.forEach(o => { booleanOptions.forEach(o => {
var input = document.getElementById(`opt-${o}`); const input = <HTMLInputElement>document.getElementById(`opt-${o}`);
addDomListener(input, 'change', () => { addDomListener(input, 'change', () => {
console.log('change', o, input.checked); console.log('change', o, input.checked);
term.setOption(o, input.checked); term.setOption(o, input.checked);
}); });
}); });
numberOptions.forEach(o => { numberOptions.forEach(o => {
var input = document.getElementById(`opt-${o}`); const input = <HTMLInputElement>document.getElementById(`opt-${o}`);
addDomListener(input, 'change', () => { addDomListener(input, 'change', () => {
console.log('change', o, input.value); console.log('change', o, input.value);
if (o === 'cols' || o === 'rows') { if (o === 'cols' || o === 'rows') {
@@ -256,7 +270,7 @@ function initOptions(term) {
}); });
}); });
Object.keys(stringOptions).forEach(o => { Object.keys(stringOptions).forEach(o => {
var input = document.getElementById(`opt-${o}`); const input = <HTMLInputElement>document.getElementById(`opt-${o}`);
addDomListener(input, 'change', () => { addDomListener(input, 'change', () => {
console.log('change', o, input.value); console.log('change', o, input.value);
term.setOption(o, input.value); term.setOption(o, input.value);
@@ -264,16 +278,16 @@ function initOptions(term) {
}); });
} }
function addDomListener(element, type, handler) { function addDomListener(element: HTMLElement, type: string, handler: (...args: any[]) => any): void {
element.addEventListener(type, handler); element.addEventListener(type, handler);
term._core.register({ dispose: () => element.removeEventListener(type, handler) }); term._core.register({ dispose: () => element.removeEventListener(type, handler) });
} }
function updateTerminalSize() { function updateTerminalSize(): void {
var cols = parseInt(document.getElementById(`opt-cols`).value, 10); const cols = parseInt((<HTMLInputElement>document.getElementById(`opt-cols`)).value, 10);
var rows = parseInt(document.getElementById(`opt-rows`).value, 10); const rows = parseInt((<HTMLInputElement>document.getElementById(`opt-rows`)).value, 10);
var width = (cols * term._core.renderer.dimensions.actualCellWidth + term._core.viewport.scrollBarWidth).toString() + 'px'; const width = (cols * term._core.renderer.dimensions.actualCellWidth + term._core.viewport.scrollBarWidth).toString() + 'px';
var height = (rows * term._core.renderer.dimensions.actualCellHeight).toString() + 'px'; const height = (rows * term._core.renderer.dimensions.actualCellHeight).toString() + 'px';
terminalContainer.style.width = width; terminalContainer.style.width = width;
terminalContainer.style.height = height; terminalContainer.style.height = height;
term.fit(); term.fit();
+1 -1
View File
@@ -34,6 +34,6 @@
<hr/> <hr/>
<p><strong>Attention:</strong> The demo is a barebones implementation and is designed for the development and evaluation of xterm.js only. Exposing the demo to the public as is would introduce security risks for the host.</p> <p><strong>Attention:</strong> The demo is a barebones implementation and is designed for the development and evaluation of xterm.js only. Exposing the demo to the public as is would introduce security risks for the host.</p>
<button id="dispose" title="This is used to testing memory leaks">Dispose terminal</button> <button id="dispose" title="This is used to testing memory leaks">Dispose terminal</button>
<script src="dist/bundle.js" defer ></script> <script src="dist/client-bundle.js" defer ></script>
</body> </body>
</html> </html>
+2 -6
View File
@@ -17,12 +17,8 @@ app.get('/style.css', function(req, res){
res.sendFile(__dirname + '/style.css'); res.sendFile(__dirname + '/style.css');
}); });
app.get('/dist/bundle.js', function(req, res){ app.get('/dist/client-bundle.js', function(req, res){
res.sendFile(__dirname + '/dist/bundle.js'); res.sendFile(__dirname + '/dist/client-bundle.js');
});
app.get('/dist/bundle.js.map', function(req, res){
res.sendFile(__dirname + '/dist/bundle.js.map');
}); });
app.post('/terminals', function (req, res) { app.post('/terminals', function (req, res) {
+49
View File
@@ -0,0 +1,49 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*
* This file is the entry point for browserify.
*/
const cp = require('child_process');
const path = require('path');
const webpack = require('webpack');
// Launch server
cp.spawn('node', [path.resolve(__dirname, 'server.js')], { stdio: 'inherit' });
// Build/watch client source
const clientConfig = {
entry: path.resolve(__dirname, 'client.ts'),
devtool: 'inline-source-map',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
},
output: {
filename: 'client-bundle.js',
path: path.resolve(__dirname, 'dist')
},
mode: 'development',
watch: true
};
const compiler = webpack(clientConfig);
compiler.watch({
// Example watchOptions
aggregateTimeout: 300,
poll: undefined
}, (err, stats) => {
// Print watch/build result here...
console.log(stats.toString({
colors: true
}));
});
+12
View File
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"rootDir": ".",
"sourceMap": true
},
"include": [
"client.ts",
"../typings/xterm.d.ts"
]
}
+4
View File
@@ -5,6 +5,10 @@
"compilerOptions": { "compilerOptions": {
"module": "commonjs", "module": "commonjs",
"target": "es5", "target": "es5",
"lib": [
"dom",
"es6"
],
"noEmit": true "noEmit": true
} }
} }
-12
View File
@@ -14,7 +14,6 @@ const source = require('vinyl-source-stream');
const sourcemaps = require('gulp-sourcemaps'); const sourcemaps = require('gulp-sourcemaps');
const ts = require('gulp-typescript'); const ts = require('gulp-typescript');
const util = require('gulp-util'); const util = require('gulp-util');
const webpack = require('webpack-stream');
const buildDir = process.env.BUILD_DIR || 'build'; const buildDir = process.env.BUILD_DIR || 'build';
const tsProject = ts.createProject('tsconfig.json'); const tsProject = ts.createProject('tsconfig.json');
@@ -137,17 +136,6 @@ gulp.task('sorcery-addons', ['browserify-addons'], function () {
}) })
}); });
gulp.task('webpack', ['build'], function() {
return gulp.src('demo/main.js')
.pipe(webpack(require('./webpack.config.js')))
.pipe(gulp.dest('demo/dist/'));
});
gulp.task('watch-demo', ['webpack'], () => {
gulp.watch(['./demo/*', './lib/**/*'], ['webpack']);
});
gulp.task('build', ['sorcery', 'sorcery-addons']); gulp.task('build', ['sorcery', 'sorcery-addons']);
gulp.task('test', ['mocha']); gulp.task('test', ['mocha']);
gulp.task('default', ['build']); gulp.task('default', ['build']);
+5 -3
View File
@@ -12,6 +12,7 @@
"@types/jsdom": "11.0.1", "@types/jsdom": "11.0.1",
"@types/mocha": "^2.2.33", "@types/mocha": "^2.2.33",
"@types/node": "6.0.108", "@types/node": "6.0.108",
"@types/webpack": "^4.4.11",
"browserify": "^13.3.0", "browserify": "^13.3.0",
"chai": "3.5.0", "chai": "3.5.0",
"concurrently": "^3.5.1", "concurrently": "^3.5.1",
@@ -36,17 +37,18 @@
"nyc": "^11.8.0", "nyc": "^11.8.0",
"sorcery": "^0.10.0", "sorcery": "^0.10.0",
"source-map-loader": "^0.2.3", "source-map-loader": "^0.2.3",
"ts-loader": "^4.5.0",
"tslint": "^5.9.1", "tslint": "^5.9.1",
"tslint-consistent-codestyle": "^1.13.0", "tslint-consistent-codestyle": "^1.13.0",
"typescript": "2.8.3", "typescript": "2.8.3",
"vinyl-buffer": "^1.0.0", "vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0", "vinyl-source-stream": "^1.1.0",
"webpack": "^3.10.0", "webpack": "^4.17.1",
"webpack-stream": "^4.0.0", "webpack-cli": "^3.1.0",
"zmodem.js": "^0.1.5" "zmodem.js": "^0.1.5"
}, },
"scripts": { "scripts": {
"start": "concurrently --kill-others-on-fail --names \"demo,server\" \"gulp watch-demo\" \"node demo/app\"", "start": "node demo/start",
"start-zmodem": "node demo/zmodem/app", "start-zmodem": "node demo/zmodem/app",
"lint": "tslint 'src/**/*.ts'", "lint": "tslint 'src/**/*.ts'",
"test": "npm-run-all mocha lint", "test": "npm-run-all mocha lint",
+4
View File
@@ -2,6 +2,10 @@
"compilerOptions": { "compilerOptions": {
"module": "commonjs", "module": "commonjs",
"target": "es5", "target": "es5",
"lib": [
"dom",
"es6",
],
"rootDir": ".", "rootDir": ".",
"outDir": "../../../lib/addons/attach/", "outDir": "../../../lib/addons/attach/",
"sourceMap": true, "sourceMap": true,
+4
View File
@@ -2,6 +2,10 @@
"compilerOptions": { "compilerOptions": {
"module": "commonjs", "module": "commonjs",
"target": "es5", "target": "es5",
"lib": [
"dom",
"es6",
],
"rootDir": ".", "rootDir": ".",
"outDir": "../../../lib/addons/fit/", "outDir": "../../../lib/addons/fit/",
"sourceMap": true, "sourceMap": true,
+4
View File
@@ -2,6 +2,10 @@
"compilerOptions": { "compilerOptions": {
"module": "commonjs", "module": "commonjs",
"target": "es5", "target": "es5",
"lib": [
"dom",
"es6",
],
"rootDir": ".", "rootDir": ".",
"outDir": "../../../lib/addons/fullscreen/", "outDir": "../../../lib/addons/fullscreen/",
"sourceMap": true, "sourceMap": true,
+4
View File
@@ -2,6 +2,10 @@
"compilerOptions": { "compilerOptions": {
"module": "commonjs", "module": "commonjs",
"target": "es5", "target": "es5",
"lib": [
"dom",
"es6",
],
"rootDir": ".", "rootDir": ".",
"outDir": "../../../lib/addons/search/", "outDir": "../../../lib/addons/search/",
"sourceMap": true, "sourceMap": true,
+4
View File
@@ -2,6 +2,10 @@
"compilerOptions": { "compilerOptions": {
"module": "commonjs", "module": "commonjs",
"target": "es5", "target": "es5",
"lib": [
"dom",
"es6",
],
"rootDir": ".", "rootDir": ".",
"outDir": "../../../lib/addons/terminado/", "outDir": "../../../lib/addons/terminado/",
"sourceMap": true, "sourceMap": true,
+4
View File
@@ -2,6 +2,10 @@
"compilerOptions": { "compilerOptions": {
"module": "commonjs", "module": "commonjs",
"target": "es5", "target": "es5",
"lib": [
"dom",
"es6",
],
"rootDir": ".", "rootDir": ".",
"outDir": "../../../lib/addons/webLinks/", "outDir": "../../../lib/addons/webLinks/",
"sourceMap": true, "sourceMap": true,
+4
View File
@@ -2,6 +2,10 @@
"compilerOptions": { "compilerOptions": {
"module": "commonjs", "module": "commonjs",
"target": "es5", "target": "es5",
"lib": [
"dom",
"es6",
],
"rootDir": ".", "rootDir": ".",
"outDir": "../../../lib/addons/winptyCompat/", "outDir": "../../../lib/addons/winptyCompat/",
"sourceMap": true, "sourceMap": true,
+4
View File
@@ -2,6 +2,10 @@
"compilerOptions": { "compilerOptions": {
"module": "commonjs", "module": "commonjs",
"target": "es5", "target": "es5",
"lib": [
"dom",
"es6",
],
"rootDir": ".", "rootDir": ".",
"outDir": "../../../lib/addons/zmodem/", "outDir": "../../../lib/addons/zmodem/",
"sourceMap": true, "sourceMap": true,
+1
View File
@@ -5,6 +5,7 @@
"lib": [ "lib": [
"dom", "dom",
"es5", "es5",
"es6",
"scripthost", "scripthost",
"es2015.promise" "es2015.promise"
], ],
-19
View File
@@ -1,19 +0,0 @@
const path = require('path');
module.exports = {
entry: './demo/main.js',
output: {
path: path.resolve(__dirname, 'demo/dist'),
filename: 'bundle.js'
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre"
}
]
}
};
+689 -219
View File
File diff suppressed because it is too large Load Diff