A speed oriented javascript browser web framework with nodejs like syntax
// cwdconst{ build }=require('jsnode');build()- all default vals/functions are fully customizable
// ./app/defaults.mjsimport{x,xrender}from'./modules/xscript.mjs';import{xutils}from'./modules/xutils.mjs';import{xviews}from'./views/xviews.mjs';import{xdata}from'./data/xdata.mjs';//cached reference to app-main objectletapp_main=x('div');// app default functionsletdefaults=Object.assign(xdata.default,{app_main: app_main,each: {before: function(dest){// tasks to be carried out prior to each rout// return false; cancel routreturntrue// continue to rout},after: function(dest){// tasks to be carried out after each routdocument.title=dest.slice(1)}},init: function(){// tasks to be carried out on initxutils.build(xdata,xviews['build'](app_main));returnthis;},render: function(stream,path,data,cb){// tasks to be carried out on renderxrender(stream,xviews[path],data,xdata[path],cb);returnthis;}})export{defaults,app_main}// app/data/xdata.mjsconstxdata={default:{version: '1.0.0',// app versionorigin: 'http://localhost:8000',// app originparams: true,// parse rout paramserror: 'error',// error handler listenerbase_path: '/index',// app base pathdelete_meta: 10000,// automatically remove meta timeout in ms || 0/falsewebmanifest: './manifest.webmanifest',// webmanifest path || falsebase_script_name: 'main',//main script name attrstyles:[{// styles to be addedhref: 'app/css/app.css',rel: 'stylesheet'}],js_head:[],// js to be added to headjs_body:[],// js to be added to bodystorage: {// state storagemax_age: 9999999999,// state storage max-age msprefix: 'rt'// state storage key prefix},stream: {// stream method defaults/fallbacksdownload: {//download fallbackstype: 'text/plain',charset: 'utf-8'},fetch: {//fetch fallbacksmethod: 'GET',headers: {'Content-Type': 'application/json'}}}},// rout default data belowindex: {msg: 'Big things have small beginnings.'},home: {msg: 'welcome message 2'}}export{xdata}import{router,x}from'./app/modules/jsnode.mjs';router.on('/',function(request,stream){stream.render('index',request.data,function(err){if(err){returnconsole.error(err)}})}).on('/home',function(request,stream){stream.render('home',request.data,function(err){if(err){returnconsole.error(err)}})}).on('error',function(err){// router error handlerconsole.log(err)}).init()// build app base defaults.init function ~ optional ~ called first.listen()// initialize router and start listening for rout calls ~ called second.validate()// check/remove stale cache entries ~ optional ~ called thirdimport{router}from'./jsnode.mjs';router.on('/',function(req,res){console.log(req)// request objectconsole.log(res)// stream object})import{router}from'./jsnode.mjs';router.on('/delete_rout',function(request,stream){}).off('/delete_rout')// delete rout '/delete_rout'router.on('/test_basic',function(request,stream){console.log(request.data)// {test:'basic'}}).on('/test_params',function(request,stream){if(request.params){console.log(request.params.get('test'))// ok}})// navigate with datarouter.rout('/test_basic',{test:'basic'})// navigate with data and paramsrouter.rout('/test_params?test=ok',{'test':'sdfsdfsd'})router.on('/',function(request,stream){stream.setCookie('name','value',{// add cookie'path': '/','secure': true,'max-age': 999999})stream.render('index',function(err){if(err){returnconsole.error(err)}//do something})}).on('/about',function(request,stream){stream.render('about',{some: 'data'},function(err){if(err){returnconsole.error(err)}//do something})})redirect path
router.on('/path1',function(request,stream){// navigate away from path1 to path2 within the site// history state is addedstream.redirect('/path2',{data: 'redirecting'})})replace state
router.on('/path1',function(request,stream){// load path2 into view without navigating away from path1// no history state is addedstream.replace('/path2',{data: 'redirecting'})})router.on('/download',function(request,stream){letdata=JSON.stringify({"test":"!@#$<}(*&^%$ok"});stream.download("test.json",// filename ~ requireddata,// file data ~ required'application/json',// content-type ~ optional'utf8'// content-encoding ~ optional);})router.on('/fetch_default',function(request,stream){//fallback to default.fetchstream.fetch('./app/data/index.json',function(err,data){if(err){returnconsole.error(err)}console.log(data.headers)// return response headers objectconsole.log(data.json)// return json dataconsole.log(data.body)// return body response})}).on('/fetch_post',function(request,stream){// cors post exampleletobj={// optional || fallback to xdata.default.stream.fetchmethod: 'post',cache: 'no-cache',headers: {'Content-Type': 'application/json','Sec-Fetch-Dest': 'object','Sec-Fetch-mode': 'cors','Sec-Fetch-Site': 'cross-site'},body: stream.js({example: 'working'})}//fallback to default.fetchstream.fetch('https/someurl.com/api',obj,function(err,data){if(err){returnconsole.error(err)}console.log(data.headers)// return response headers objectconsole.log(data.json)// return json dataconsole.log(data.body)// return body response})})router.on('/',function(request,stream){console.log(request)if(request.params){console.log(request.params.get('test'))// get params by keyconsole.log(params.getAll('foo'))// get all of key ["1","4"].console.log(params.has('bar')===true);// true/false param existsconsole.log(params.keys())// return params keysconsole.log(params.values())// return params valuesparams.set('baz',3)// set paramsparams.append('foo',4);// add to paramsparams.toString()// return params as stringparams.sort();// sort paramsparams.delete('foo')// delete a paramparams.entries()params.forEach()}})router.on('/',function(r,s){letdata={test: 'json.stringify'}console.log(s.js(data))// '{"test":"json.stringify"}'console.log(s.js(data,0,2))/* { "test":"json.stringify" } */})router.on('/',function(r,s){letdata='{"test":"json.parse"}'console.log(s.jp(data))// {test: "json.parse"}})router.on('/',function(request,stream){stream.empty()// remove app_main childNodesstream.empty(document.body)// remove any elements childNodes})router.on('/',function(request,stream){// append elements to app-mainstream.append(x('p','text appended to app-main'))})router.on('/example',function(request,stream){// return parsed path detailsconsole.log(stream.path(request.data.filename))//{fileName: "file.js", baseName: "file", ext: "js", dirName: "/path/to"}})router.rout('/example',{filename: '/path/to/file.js'})router.on('/example',function(request,stream){// create blob object from dataconsole.log(stream.blob(...request.data.blob))//Blob {size: 9, type: "plain/text;utf-8"}})router.rout('/example',{blob: ['some test','plain/text','utf-8']})router.on('/example',function(request,stream){// return parsed url objectconsole.log(stream.url.parse(request.data.path))/* { hash: "" host: "www.someurl.com" hostname: "www.someurl.com" href: "https://www.someurl.com/index.js" origin: "https://www.someurl.com" password: "" pathname: "/index.js" port: "" protocol: "https:" search: "" searchParams: URLSearchParams {} username: "" } */})router.rout('/example',{path: 'https://www.someurl.com/index.js'})router.on('/example',function(request,stream){// create new url from blob objectletnewUrl=stream.url.add(...request.data.url);document.body.append(x('a',{href: newUrl},'test-link'))})router.rout('/example',{url: ['some test','plain/text','utf-8']})router.on('/example',function(request,stream){// create new url from blob objectletnewUrl=stream.url.add(...request.data.url),lnk=x('a',{href: newUrl,onclick: function(){lnk.remove();setTimeout(function(){// delete new url linkstream.url.del(newUrl);},3000)}},'test-link');document.body.append(lnk)})router.rout('/example',{url: ['some test','plain/text','utf-8']})- stream.setCookie
- stream.getCookie
- stream.delCookie
router.on('/',function(request,stream){// add cookiestream.setCookie('name','value',{'path': '/','secure': true,'max-age': 999999}).delCookie('name')// delete cookieconsole.log(stream.getCookie('name'))// get a cookie})- stream.ssSet
- stream.ssGet
- stream.ssDel
router.on('/',function(request,stream){stream.ssSet('key',{test: 'working'})// set stringified session storage.ssDel('key')// delete session storage itemconsole.log(stream.ssGet('key'))// get parsed session storage})- stream.lsSet
- stream.lsGet
- stream.lsDel
router.on('/',function(request,stream){stream.lsSet('key',{test: 'working'})// set stringified local storage.lsDel('key')// delete local storage itemconsole.log(stream.lsGet('key'))// get parsed local storage})xscript is the pure javascript template engine used for jsnode
- xscript works directly with the dom
- xscript uses no regex/unsafe functions
- xscript is about as close to using vanilla js speed as it gets.
/** * @x(tag, ...arguments) * @param {string} tag ~ html tag * @param {object|string|function} arguments **/// create a basic element with textletitem=x('p','example plain text');console.log(item)// <p>example plain text</p>document.body.append(item)// create an element with multiple attributesletitem=x('input',{id: 'testid',class: 'class1 class2 class3',type: 'text',placeHolder: 'example attributes',style: 'color:red;background:black'})console.log(item)// <input id="testid" class="class1 class2 class3" type="text" style="color:red;background:black" placeholder="example attributes">letitem=x('p',{id: 'testid',class: 'class1 class2 class3',onclick: function(){console.log('item clicked!');},onmouseover: function(){console.log('item mouseover!');}})document.body.append(item);item.click()// item clicked!item.onmouseover()// item mouseover!letitems=x('p',x('p','level 2.1'),"some text",x('p','level 2.2',x('p','level 3',x('p','level 4.1'),function(){// some function elementreturnx('p','level 4.2')},x('p','level 4.3'),function(){return'some function text node'})),'level 1')document.body.append(items);once created, an node can be referenced like any other js node.
letitem=x('p')item.id='testid';item.textContent='click me';letsomefunction=function(){item.removeEventListener('click',somefunction);item.textContent='clicked'somefunction=item=null;console.log(somefunction,item)}item.addEventListener('click',somefunction,false)document.body.append(item);letitems=x('p','prepended text',x('p','basic 1','basic 2',()=>'basic 3'),'nested text',x('p','basic 4','basic 5',()=>'basic 6'),'appended text text');console.log(items);/*<p> "prepended text" <p>"basic 1" "basic 2" "basic 3"</p> "nested text" <p>"basic 4" "basic 5" "basic 6"</p> "appended text text"</p>*/- functions should only return element or text nodes.
letitems=x('p',()=>'you',()=>'can',()=>'add',()=>'as',()=>'many',()=>x('p','as'),()=>'you',()=>'want',()=>'wherever',()=>'you',()=>'want',);console.log(items);