mirror of
				https://gitlab.com/SpaccInc/SpaccDotWeb.git
				synced 2025-06-05 21:29:12 +02:00 
			
		
		
		
	Fix non-critical errors live mode, add transposing of standalone HTML elements to final DOM, add build-time rendering
This commit is contained in:
		
							
								
								
									
										17
									
								
								.gitlab-ci.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.gitlab-ci.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| image: alpine:latest | ||||
|  | ||||
| before_script: | | ||||
|   apk update | ||||
|   apk add nodejs npm | ||||
|  | ||||
| pages: | ||||
|   stage: deploy | ||||
|   script: | | ||||
|     npm install | ||||
|     npm run build:lib | ||||
|     node ./SpaccDotWeb.js 'SpaccDotWeb.AppBuildStandalone({ Page: "Example.html" })' | ||||
|     mkdir -p ./public | ||||
|     mv ./Build/* ./public | ||||
|   artifacts: | ||||
|     paths: | ||||
|     - public | ||||
							
								
								
									
										33
									
								
								Example.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Example.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| <!DOCTYPE html> | ||||
| <script src="SpaccDotWeb.js" module="SpaccDotWeb"></script> | ||||
|  | ||||
| <script module="Meta">({ | ||||
| 	"Name": "The Testing", | ||||
| 	"Description": "Useful for doing The Testing", | ||||
| 	"Uri": "https://example.com/Testing", | ||||
| })</script> | ||||
|  | ||||
| <script module="Main"> | ||||
| 	const Spacc = SpaccDotWeb.AppInit(); | ||||
|  | ||||
| 	Spacc.Select('#App').innerHTML = ` | ||||
| 		<p>Click to log!</p> | ||||
| 		<button>Log</button> | ||||
| 	`; | ||||
|  | ||||
| 	Spacc.Select('#App button').onclick = () => { | ||||
| 		Spacc.Select('#App').innerHTML += `<xmp style="white-space: break-spaces;"> | ||||
| 			${(document.documentElement.outerHTML)} | ||||
| 		</xmp>`; | ||||
| 	}; | ||||
| </script> | ||||
|  | ||||
| <script module="Testing"> | ||||
| 	Spacc.Select('#App').Insert(Spacc.Create('p', { innerHTML: `<b>The auto-testing is complete.</b>` })); | ||||
| </script> | ||||
|  | ||||
| <style> | ||||
| 	body { | ||||
| 		background: lightgray; | ||||
| 	} | ||||
| </style> | ||||
| @@ -1,27 +0,0 @@ | ||||
| <script src="SpaccDotWeb.js"></script> | ||||
|  | ||||
| <script id="Meta" type="text/json">{ | ||||
| 	"Name": "The Testing", | ||||
| 	"Description": "Useful for doing The Testing", | ||||
| 	"Uri": "https://example.com/Testing" | ||||
| }</script> | ||||
|  | ||||
| <script module="Main"> | ||||
| 	const Spacc = SpaccDotWeb; | ||||
| 	Spacc.AppInit(); | ||||
|  | ||||
| 	Spacc.Select('#App').innerHTML = ` | ||||
| 		<p>1234</p> | ||||
| 		<button>Log</button> | ||||
| 	`; | ||||
|  | ||||
| 	Spacc.Select('#App button').onclick = () => { | ||||
| 		Spacc.Select('#App').innerHTML += `<xmp style="text-wrap: unset;"> | ||||
| 			${JSON.stringify(document.documentElement.outerHTML)} | ||||
| 		</xmp>`; | ||||
| 	}; | ||||
| </script> | ||||
|  | ||||
| <script module="Testing"> | ||||
| 	Spacc.Select('#App').Insert(Spacc.Create('p', { innerHTML: `<b>Testing is complete.</b>` })); | ||||
| </script> | ||||
| @@ -8,14 +8,16 @@ let __scriptname; | ||||
|  | ||||
| const platformIsNode = (typeof module === 'object' && typeof module.exports === 'object'); | ||||
| const platformIsBrowser = (typeof window !== 'undefined' && typeof window.document !== 'undefined'); | ||||
| const __filename__ = (typeof __filename !== 'undefined' ? __filename : ''); | ||||
| const JsdomOptions = { /*resources: "usable",*/ runScripts: /*"dangerously"*/"outside-only" }; | ||||
|  | ||||
| if (platformIsNode) { | ||||
| 	Lib.fs = require('fs'); | ||||
| 	Lib.crypto = require('crypto'); | ||||
| 	Lib.childProcess = require('child_process'); | ||||
| 	Lib.jsdom = require('jsdom'); | ||||
| 	__scriptname = __filename.split('/').slice(-1)[0]; | ||||
| 	windowObject = new Lib.jsdom.JSDOM().window; | ||||
| 	__scriptname = __filename__.split('/').slice(-1)[0]; | ||||
| 	windowObject = new Lib.jsdom.JSDOM('', JsdomOptions).window; | ||||
| }; | ||||
|  | ||||
| if (platformIsBrowser) { | ||||
| @@ -31,7 +33,7 @@ const SpaccDotWeb = ((args) => { ////////////////////////////////////////////// | ||||
| let SpaccDotWeb = {}; | ||||
|  | ||||
| if (platformIsNode) { | ||||
| 	SpaccDotWeb.AppBuildStandalone = (opts) => { | ||||
| 	SpaccDotWeb.AppBuildStandalone = (opts) => { // TODO: build result of dom after JS, to make base page usable without JS | ||||
| 		isBuildingApp = true; | ||||
| 		opts ||= {}; | ||||
| 		opts.Page ||= 'index.html'; | ||||
| @@ -40,7 +42,7 @@ if (platformIsNode) { | ||||
| 		Lib.fs.mkdirSync(`${__dirname}/Build/App-${opts.Page}`, { recursive: true }); | ||||
| 		let htmlIndex = Lib.fs.readFileSync(opts.Page, 'utf8'); | ||||
|  | ||||
| 		windowObject = new Lib.jsdom.JSDOM(htmlIndex).window; | ||||
| 		windowObject = new Lib.jsdom.JSDOM(htmlIndex, JsdomOptions).window; | ||||
| 		documentObject = windowObject.document; | ||||
|  | ||||
| 		DomSetup(opts.Modules); | ||||
| @@ -56,11 +58,11 @@ if (platformIsNode) { | ||||
| 		const minifiedPath = `${__dirname}/Build/SpaccDotWeb.min.js`; | ||||
| 		const hashPath = `${__dirname}/Build/SpaccDotWeb.js.hash`; | ||||
| 		const hashOld = (Lib.fs.existsSync(hashPath) && Lib.fs.readFileSync(hashPath, 'utf8')); | ||||
| 		const hashNew = Lib.crypto.createHash('sha256').update(Lib.fs.readFileSync(__filename, 'utf8')).digest('hex'); | ||||
| 		const hashNew = Lib.crypto.createHash('sha256').update(Lib.fs.readFileSync(__filename__, 'utf8')).digest('hex'); | ||||
| 		if (!Lib.fs.existsSync(compiledPath) || !Lib.fs.existsSync(minifiedPath) || !(hashOld === hashNew)) { | ||||
| 			uptodate = false; | ||||
| 			Lib.fs.writeFileSync(hashPath, hashNew); | ||||
| 			Lib.fs.writeFileSync(compiledPath, Lib.childProcess.execSync(`cat "${__filename}" | npx babel -f "${__scriptname}"`)); | ||||
| 			Lib.fs.writeFileSync(compiledPath, Lib.childProcess.execSync(`cat "${__filename__}" | npx babel -f "${__scriptname}"`)); | ||||
| 			Lib.fs.writeFileSync(minifiedPath, Lib.childProcess.execSync(`cat "${compiledPath}" | npx uglifyjs`)); | ||||
| 		}; | ||||
| 		uptodate && console.log('Library is up-to-date.'); | ||||
| @@ -68,9 +70,10 @@ if (platformIsNode) { | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| SpaccDotWeb.AppInit = function AppInit(){ | ||||
| SpaccDotWeb.AppInit = () => { | ||||
| 	try { | ||||
| 		DomSetup(); | ||||
| 		return SpaccDotWeb; | ||||
| 	} catch(err) { console.log(err) }; | ||||
| }; | ||||
|  | ||||
| @@ -90,35 +93,65 @@ SpaccDotWeb.Select = (query) => { | ||||
| 	return elem; | ||||
| }; | ||||
|  | ||||
| const AppMetaGet = () => JSON.parse(SpaccDotWeb.Select('#Meta').innerHTML); | ||||
| SpaccDotWeb.RequireScript = (src) => { | ||||
| 	if (platformIsBrowser) { | ||||
| 		SpaccDotWeb.Select('body').Insert(SpaccDotWeb.Create('script', { src: src })); | ||||
| 	//} else if (platformIsNode) { | ||||
| 	//	require(src); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| // TODO: make Meta element optional without breaking things | ||||
| const AppMetaGet = () => { | ||||
| 	const elem = SpaccDotWeb.Select('script[module="Meta"]'); | ||||
| 	if (elem) { | ||||
| 		if (['application/json', 'text/json'].includes(elem.getAttribute('type'))) { | ||||
| 			return JSON.parse(elem.innerHTML); | ||||
| 		} else { | ||||
| 			return eval(elem.innerHTML); | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| const DomMakeBase = (Modules) => { | ||||
| 	const meta = AppMetaGet(); | ||||
|  | ||||
| 	if (meta) { | ||||
| 		const htmlFrags = { | ||||
| 			Title: (meta.Name ? `<title>${meta.Name}</title><meta property="og:title" content="${meta.Name}"/>` : ''), | ||||
| 			Description: (meta.Description ? `<meta name="description" content="${meta.Description}"/><meta property="og:description" content="${meta.Description}"/>` : ''), | ||||
| 			Uri: (meta.Uri ? `<link rel="canonical" href="${meta.Uri}"/><meta property="og:url" content="${meta.Uri}"/>` : '') | ||||
| 		}; | ||||
|  | ||||
| 	let scripts = ''; | ||||
| 		let [scriptsCode, elementsHtml, scriptsHtml] = ['', '', '']; | ||||
|  | ||||
| 		if (isBuildingApp) { | ||||
| 		scripts += `<scr`+`ipt src="http://cdn.jsdelivr.net/npm/core-js-bundle/minified.min.js"></scr`+`ipt>`; | ||||
| 		scripts += `<scr`+`ipt src="https://cdn.jsdelivr.net/npm/core-js-bundle/minified.min.js"></scr`+`ipt>`; | ||||
| 		scripts += `<scr`+`ipt>${SpaccDotWeb.LibBuild().minified}</scr`+`ipt>`; | ||||
| 			scriptsHtml += `<scr`+`ipt src="http://cdn.jsdelivr.net/npm/core-js-bundle/minified.min.js"></scr`+`ipt>`; | ||||
| 			scriptsHtml += `<scr`+`ipt src="https://cdn.jsdelivr.net/npm/core-js-bundle/minified.min.js"></scr`+`ipt>`; | ||||
| 			scriptsHtml += `<scr`+`ipt>${SpaccDotWeb.LibBuild().minified}</scr`+`ipt>`; | ||||
| 			for (const elem of documentObject.querySelectorAll('script[module]')) { | ||||
| 			if (!Modules || (Modules && Modules.includes(elem.getAttribute('module')))) { | ||||
| 				//if (elem.module === 'Meta' && !['application/json', 'text/json'].includes(elem.type)) { | ||||
| 				//	elem.innerHTML = `(${elem.innerHTML})`; | ||||
| 				//}; | ||||
| 				if (elem.getAttribute('module') !== 'SpaccDotWeb' && (!Modules || (Modules && Modules.includes(elem.getAttribute('module'))))) { | ||||
| 					if (elem.getAttribute('src')) { | ||||
| 					scripts += `<scr`+`ipt src="${elem.getAttribute('src')}"></scr`+`ipt>` | ||||
| 						scriptsHtml += `<scr`+`ipt src="${elem.getAttribute('src')}"></scr`+`ipt>`; | ||||
| 						// TODO somehow include this in prerendered DOM? | ||||
| 					} else { | ||||
| 						const tmpHash = Lib.crypto.createHash('sha256').update(elem.innerHTML).digest('hex'); | ||||
| 						const tmpPath = `${__dirname}/Build/Assets.tmp/${tmpHash}.js`; | ||||
| 						Lib.fs.writeFileSync(tmpPath, elem.innerHTML); | ||||
| 					scripts += `<scr`+`ipt>${Lib.childProcess.execSync(`cat "${tmpPath}" | npx babel -f "${tmpHash}.js" | npx uglifyjs`)}</scr`+`ipt>`; | ||||
| 						const scriptCode = Lib.childProcess.execSync(`cat "${tmpPath}" | npx babel -f "${tmpHash}.js" | npx uglifyjs`); | ||||
| 						scriptsCode += scriptCode; | ||||
| 						scriptsHtml += `<scr`+`ipt>${Lib.childProcess.execSync(`cat "${tmpPath}" | npx babel -f "${tmpHash}.js" | npx uglifyjs`)}</scr`+`ipt>`; | ||||
| 					}; | ||||
| 				}; | ||||
| 				elem.remove(); | ||||
| 			}; | ||||
| 			// select and include all remaining actual elements, except parent meta-elements | ||||
| 			for (const elem of documentObject.querySelectorAll('* > * > *')) { | ||||
| 				elementsHtml += elem.outerHTML; | ||||
| 			} | ||||
| 		}; | ||||
|  | ||||
| 		return { | ||||
| @@ -129,7 +162,9 @@ const DomMakeBase = (Modules) => { | ||||
| 				${htmlFrags.Description} | ||||
| 				${htmlFrags.Uri} | ||||
| 			`, | ||||
| 		body: `<div id="App"></div>${scripts}`, | ||||
| 			body: `<div id="App"></div>${elementsHtml}${scriptsHtml}`, | ||||
| 			scriptsCode: scriptsCode, | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| @@ -139,9 +174,14 @@ const DomSetup = (Modules) => { | ||||
| 		? documentObject.replaceChild(doctypeNew, documentObject.doctype) | ||||
| 		: documentObject.insertBefore(doctypeNew, documentObject.childNodes[0]); | ||||
| 	const domBase = DomMakeBase(Modules); | ||||
| 	if (domBase) { | ||||
| 		documentObject.write(domBase.head + domBase.body); | ||||
| 		documentObject.head.innerHTML = domBase.head; | ||||
| 		documentObject.body.innerHTML = domBase.body; | ||||
| 		if (isBuildingApp) { | ||||
| 			windowObject.eval(Lib.fs.readFileSync(__filename__, 'utf-8') + domBase.scriptsCode); | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| return SpaccDotWeb; | ||||
| @@ -151,7 +191,7 @@ return SpaccDotWeb; | ||||
|  | ||||
|  | ||||
| platformIsBrowser && (window.SpaccDotWeb = SpaccDotWeb); | ||||
| platformIsNode && (console.log(eval(process.argv.slice(-1)[0]))); | ||||
| platformIsNode && process.argv.length >= 2 && console.log(eval(process.argv.slice(-1)[0])); | ||||
|  | ||||
| return SpaccDotWeb; | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| 	"name": "SpaccDotWeb", | ||||
| 	"version": "indev", | ||||
| 	"scripts": { | ||||
| 		"build:lib": "node ./SpaccDotWeb.js 'SpaccDotWeb.LibBuild()'", | ||||
| 		"build:lib": "sh -c \"node ./SpaccDotWeb.js 'SpaccDotWeb.LibBuild()'\"", | ||||
| 		"build:clear": "rm -rf ./Build" | ||||
| 	}, | ||||
| 	"devDependencies": { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user