This is the 21st day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

wedge

What? Can the browser still be a Web server?

Cut the gossip and get straight to work!

The whole idea: Server Workers in PWA for caching files can dynamically generate new files and fetch events to send them to the browser!

  • Those who are not familiar with PWA can simply understand the following:

PWA (Progressive Web Apps), which translates as Progressive Web applications, is a new concept for building Web applications that involve specific patterns, apis, and other features.

It can do things that the traditional Web can’t: work offline, install, synchronize easily, send push notifications, etc.

  • For those who are not familiar with Server workers, you can simply know as follows:

Server workers are the middleman between the server and browser. If a service worker is registered in the website, it can intercept all the requests of the current website and make judgment (corresponding judgment program needs to be written). If it needs to send requests to the server, it will transfer them to the server. If you can use the cache directly, you just return it to the cache and don’t transfer it to the server. Thus greatly enhancing the browsing experience.

So, server Workers can send files to the browser!

tool

  • We also have somorphic-git

Somorphic-git is a pure JavaScript implementation of Git for Node and browser environments (including WebWorkers and ServiceWorkers);

It can be used to read and write Git libraries, as well as fetching and pushing from Github.

  • We also have BrowserFS

BrowserFS, like Webpack, is a module packaging tool;

Its features:

  1. Design based on stream idea
  2. It can be used either through the Command line or through the API
  3. Only deal with javascript
  4. Modularity is the reverse process, but it promotes the better development of modularity
  5. Some Node core Modules are built in
  6. The Node module can be used in the browser and is a powerful weapon for homogeneous applications
  • We also have lightning-FS

It makes browser files read and write faster, as fast as lightning ⚡~

  • Finally, we need an indexedDB

IndexedDB can store large amounts of structured data on the client

implementation

Code implementation:

/**@license * ___ ___ _____ __ __ _ _____ _ _ * / __|_ _|_ _| \ \ / /__| |__ |_ _|__ _ _ _ __ (_)_ _ __ _| | * | (_ || |  | | \ // / -_) '_ \ | |/ -_) '_| ' | | ' / _` | | * ___|___| |_| _/_/___|_.__/ |_|___|_| |_|_|_|_|_||___,_|_| * * this is service worker and it's part of GIT Web terminal * * Copyright (c) 2018 Jakub Jankiewicz <http://jcubic.pl/me> * Released under the MIT license * */ /* global BrowserFS, Response, setTimeout, fetch, Blob, Headers */ self.importScripts('https://cdn.jsdelivr.net/npm/browserfs'); self.addEventListener('install', self.skipWaiting); self.addEventListener('activate', self.skipWaiting); self.addEventListener('fetch', function (event) { let path = BrowserFS.BFSRequire('path'); let fs = new Promise(function(resolve, reject) { BrowserFS.configure({ fs: 'IndexedDB', options: {} }, function (err) { if (err) { reject(err); } else { resolve(BrowserFS.BFSRequire('fs')); }}); }); event.respondWith(fs.then(function(fs) { return new Promise(function(resolve, reject) { function sendFile(path) { fs.readFile(path, function(err, buffer) { if (err) { err.fn = 'readFile(' + path + ')'; return reject(err); } var ext = path.replace(/.*./, ''); var mime = { 'html': 'text/html', 'json': 'application/json', 'js': 'application/javascript', 'css': 'text/css' }; var headers = new Headers({ 'Content-Type': mime[ext] }); resolve(new Response(buffer, {headers})); }); } var url = event.request.url; var m = url.match(/__browserfs__(.*)/); function redirect_dir() { return resolve(Response.redirect(url + '/', 301)); } function serve() { fs.stat(path, function(err, stat) { if (err) { return resolve(textResponse(error404Page(path))); } if (stat.isFile()) { sendFile(path); } else if (stat.isDirectory()) { if (path.substr(-1, 1) ! == '/') { return redirect_dir(); } fs.readdir(path, function(err, list) { if (err) { err.fn = 'readdir(' + path + ')'; return reject(err); } var len = list.length; if (list.includes('index.html')) { sendFile(path + '/index.html'); } else { listDirectory({fs, path, list}).then(function(list) { resolve(textResponse(fileListingPage(path, list))); }).catch(reject); }}); }}); } if (m) { var path = m[1]; if (path === '') { return redirect_dir(); } console.log('serving ' + path + ' from browserfs'); serve(); } else { if (event.request.cache === 'only-if-cached' && event.request.mode ! == 'same-origin') { return; } //request = credentials: 'include' fetch(event.request).then(resolve).catch(reject); }}); })); }); // ----------------------------------------------------------------------------- function listDirectory({fs, path, list}) { return new Promise(function(resolve, reject) { var items = []; (function loop() { var item = list.shift(); if (! item) { return resolve(items); } fs.stat(path + '/' + item, function(err, stat) { if (err) { err.fn = 'stat(' + path + '/' + item + ')'; return reject(err); } items.push(stat.isDirectory() ? item + '/' : item); loop(); }); }) (); }); } // ----------------------------------------------------------------------------- function textResponse(string, filename) { var blob = new Blob([string], { type: 'text/html' }); return new Response(blob); } // ----------------------------------------------------------------------------- function fileListingPage(path, list) { var output = [ '<!DOCTYPE html>', '<html>', '<body>', `<h1>BrowserFS ${path}</h1>`, '<ul>' ]; if (path.match(/^/(.*/)/)) { output.push('<li><a href=".." >.. </a></li>'); } list.forEach(function(name) { output.push('<li><a href="' + name + '">' + name + '</a></li>'); }); output = output.concat(['</ul>', '</body>', '</html>']); return output.join('\n'); } // ----------------------------------------------------------------------------- function error404Page(path) { var output = [ '<! DOCTYPE html>', '<html>', '<body>', '<h1>404 File Not Found</h1>', `<p>File ${path} not found in browserfs`, '</body>', '</html>' ]; return output.join('\n'); }Copy the code

Initialize the service worker as follows:

if ('serviceWorker' in navigator) {

var scope = location.pathname.replace(/\/[^\/]+$/, '/');

if (!scope.match(/__browserfs__/)) {

navigator.serviceWorker.register('sw.js', {scope})

.then(function(reg) {

reg.addEventListener('updatefound', function() {

var installingWorker = reg.installing;

console.log('A new service worker is being installed:',

installingWorker);

});

// registration worked

console.log('Registration succeeded. Scope is ' +

reg.scope);

}).catch(function(error) {

// registration failed

console.log('Registration failed with ' + error);

});

}

}

test

Jakub T. Jankiewicz also created an online test called GIT Web Terminal. What else can I say?

Calling tests:

vi test.txt

i Hello World :wq

view test.txt
Copy the code

View return:

To be fair, this is a good attempt in this direction

Enhancing Web capabilities is our duty! (low low ◡)


OK, the above is the share ~ writing is not easy, like encourage 👍👍👍

Reference for this article:

  • Itnext. IO/how-to – crea…
  • browserify
  • PWA

I am Anthony Nuggets, the public account of the same name, every day a pawn, dig a gold, goodbye ~