@liascript/devserver
Version:
Run a development server for LiaScript locally
256 lines (255 loc) • 10.6 kB
JavaScript
;
exports.__esModule = true;
exports.gotoLine = exports.stop = exports.start = exports.init = void 0;
require('dotenv').config();
var express = require("express");
var fs = require("fs");
var path = require("path");
var cors = require('cors');
var handlebars = require('express-handlebars');
var ip = require('ip');
var open = require('open');
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.json());
var dirname = '';
var node_modules;
var reloadPath = '';
var liascriptPath = '';
var clients = [];
var gotoScript = "<script>\nif (!window.LIA) {\n window.LIA = {}\n}\n\nvar filename__ = document.location.search.replace(\"?\"+document.location.origin, \"\")\n\nwindow.LIA.lineGoto = function(linenumber) {\n fetch(\"/lineGoto\", {\n method: \"POST\",\n headers: {'Content-Type': 'application/json'}, \n body: JSON.stringify({\n \"linenumber\": linenumber,\n \"filename\": filename__\n })\n }).then(res => {\n console.log(\"Goto line\", linenumber);\n });\n}\n\nconst events = new EventSource('/gotoLine');\nevents.onmessage = (event) => {\n try {\n const data = JSON.parse(event.data);\n if (data.filename == filename__) {\n console.log(\"goto line:\", data.linenumber);\n window.LIA.gotoLine(data.linenumber)\n }\n } catch (e) {\n console.warn(\"gotoLine failed\")\n }\n};\n</script>";
var serverPointer;
init(__dirname);
function init(serverPath, nodeModulesPath) {
dirname = serverPath || path.join(__dirname, '..');
node_modules = nodeModulesPath || path.join(dirname, 'node_modules');
reloadPath = path.resolve(path.join(node_modules, 'reloadsh.js/reloader.browser.js'));
liascriptPath = path.resolve(path.join(node_modules, '@liascript/editor/dist'));
}
exports.init = init;
function start(port, hostname, input, responsiveVoice, liveReload, openInBrowser, testOnline, gotoCallback) {
port = port || 3000;
hostname = hostname || 'localhost';
openInBrowser = openInBrowser || false;
input = input || '.';
liveReload = liveReload || false;
testOnline = testOnline || false;
var project = {
path: input,
readme: undefined
};
if (input) {
var stats = fs.lstatSync(input);
// Is it a directory?
if (stats.isDirectory()) {
project.path = input;
}
else if (stats.isFile()) {
project.path = path.dirname(input);
project.readme = path.basename(input);
}
}
app.set('view engine', 'hbs');
app.engine('hbs', handlebars({
layoutsDir: path.resolve(path.join(dirname, 'views/layouts')),
defaultLayout: 'main',
extname: 'hbs'
}));
app.set('views', path.resolve(path.join(dirname, 'views')));
app.get('/', function (req, res) {
res.redirect('/home');
});
app.get('/gotoLine', eventsHandler);
app.get('/home*', function (req, res) {
var currentPath = project.path + '/' + req.params[0];
var stats = fs.lstatSync(currentPath);
// Is it a directory?
if (stats.isDirectory()) {
var files = fs.readdirSync(currentPath).filter(function (e) {
return e[0] !== '.';
});
var basePath = '/home';
var pathNames = req.params[0].split('/').filter(function (e) {
return e !== '';
});
var paths = [];
for (var i = 0; i < pathNames.length; i++) {
basePath += '/' + pathNames[i];
paths.push({ name: pathNames[i], href: basePath });
}
res.render('main', {
layout: 'index',
path: paths,
file: files
.map(function (file) {
return {
name: file,
href: "http://".concat(hostname, ":").concat(port, "/home").concat(req.params[0], "/").concat(file),
isDirectory: fs.lstatSync(currentPath + '/' + file).isDirectory()
};
})
.sort(function (a, b) {
if (a.isDirectory && !b.isDirectory) {
return -1;
}
else if (!a.isDirectory && b.isDirectory) {
return 1;
}
else {
if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) {
return -1;
}
else {
return 1;
}
}
return 0;
})
});
}
else if (stats.isFile()) {
if (req.params[0].toLocaleLowerCase().endsWith('.md')) {
if (testOnline) {
res.redirect("https://LiaScript.github.io/course/?http://".concat(hostname, ":").concat(port, "/").concat(req.params[0]));
}
else {
res.redirect("/liascript/index.html?http://".concat(hostname, ":").concat(port, "/").concat(req.params[0]));
}
}
else {
res.sendFile(req.params[0], { root: project.path });
}
}
else {
res.send('ups, something went wrong');
}
});
app.get('/liascript/', function (req, res) {
res.redirect('/liascript/index.html');
});
app.get('/liascript/index.html', function (req, res) {
// ------------------------------------
if (liveReload && responsiveVoice) {
fs.readFile(liascriptPath + '/index.html', 'utf8', function (err, data) {
res.send(data.replace('</head>', "<script type='text/javascript' src='/reloader/reloader.js'></script>\n <script type='text/javascript' src='https://code.responsivevoice.org/responsivevoice.js?key=".concat(responsiveVoice, "'></script>\n ").concat(gotoScript, "\n </head>")));
});
}
// ------------------------------------
else if (liveReload) {
fs.readFile(liascriptPath + '/index.html', 'utf8', function (err, data) {
res.send(data.replace('</head>', "<script type='text/javascript' src='/reloader/reloader.js'></script>\n ".concat(gotoScript, "\n </head>")));
});
}
// ------------------------------------
else if (responsiveVoice) {
fs.readFile(liascriptPath + '/index.html', 'utf8', function (err, data) {
res.send(data.replace('</head>', "<script type='text/javascript' src='https://code.responsivevoice.org/responsivevoice.js?key=".concat(responsiveVoice, "'></script>\n ").concat(gotoScript, "\n </head>")));
});
}
// ------------------------------------
else {
fs.readFile(liascriptPath + '/index.html', 'utf8', function (err, data) {
res.send(data.replace('</head>', "".concat(gotoScript, "</head>")));
});
}
});
// load everything from the liascript folder
app.get('/liascript/*', function (req, res) {
res.sendFile(req.params[0], { root: liascriptPath });
});
// ignore this one
app.get('/sw.js', function (req, res) { });
app.get('/favicon.ico', function (req, res) { });
// pass the reloader, to be used for live updates
app.get('/reloader/reloader.js', function (req, res) {
res.sendFile(reloadPath);
});
// react to click-events
app.post('/lineGoto', function (req, res) {
if (gotoCallback) {
try {
var linenumber = req.body.linenumber;
var filename = req.body.filename;
gotoCallback(linenumber, filename);
}
catch (e) {
console.warn("lineGoto event with wrong datatype, you have to provide {'linenumber': int, 'filename': string}");
}
}
return res.json({});
});
// everything else comes from the current project folder
app.get('/*', cors(), function (req, res) {
res.sendFile(req.originalUrl, { root: project.path });
});
var localURL = 'http://' + hostname + ':' + port;
if (project.path && project.readme) {
localURL +=
'/liascript/index.html?http://' +
hostname +
':' +
port +
'/' +
project.readme;
}
if (testOnline && project.readme) {
localURL =
'https://LiaScript.github.io/course/?http://' +
hostname +
':' +
port +
'/' +
project.readme;
}
var server = require('reloadsh.js')(app, liveReload ? [path.join(project.path, project.readme || '')] : []);
if (liveReload) {
console.log("\u2728 watching for changes on: \"".concat(path.join(project.path || '', project.readme || ''), "\""));
}
server.on('error', function (e) {
throw e;
});
server.listen(port);
if (openInBrowser) {
open(localURL);
}
console.log('📡 starting server');
console.log(" - local: ".concat(localURL));
console.log(" - on your network: ".concat(localURL.replace(hostname, ip.address())));
serverPointer = server;
}
exports.start = start;
function stop() {
if (serverPointer) {
serverPointer.close();
}
}
exports.stop = stop;
function gotoLine(linenumber, filename) {
clients.forEach(function (client) {
return client.response.write("data: ".concat(JSON.stringify({
linenumber: linenumber,
filename: filename
}), "\n\n"));
});
}
exports.gotoLine = gotoLine;
function eventsHandler(request, response, next) {
var headers = {
'Content-Type': 'text/event-stream',
Connection: 'keep-alive',
'Cache-Control': 'no-cache'
};
response.writeHead(200, headers);
var data = "data: \n\n";
response.write(data);
var clientId = Date.now();
var newClient = {
id: clientId,
response: response
};
clients.push(newClient);
request.on('close', function () {
console.log("".concat(clientId, " Connection closed"));
clients = clients.filter(function (client) { return client.id !== clientId; });
});
}