als-render
Advanced tools
Comparing version 0.5.0 to 0.6.0
@@ -10,3 +10,3 @@ ## Usage | ||
const app = express() | ||
const render = require('als-render'); // Path to the ALS-Render library | ||
const Render = require('als-render'); | ||
@@ -28,3 +28,3 @@ const layout = (rawHtml,bundle) => `<!DOCTYPE html> | ||
const path = './path/to/your/JSXComponent'; | ||
const contextName = 'context'; | ||
Render.contextName = 'context'; | ||
const minified = false; | ||
@@ -34,3 +34,3 @@ | ||
const data = {}; | ||
const { rawHtml, bundle } = render(path,data,contextName,minified); | ||
const { rawHtml, bundle } = Render.render(path,data,minified); | ||
res.end(layout(rawHtml, bundle)) | ||
@@ -37,0 +37,0 @@ }) |
@@ -10,3 +10,3 @@ ## ALS-Render Features Overview | ||
* **Event Handling** - Event handling in ALS-Render is streamlined by automating the addition of event listeners. All event functions are stored in `context.actions` and are automatically attached to elements as listeners after each update. This setup allows for a more declarative attachment of events compared to manual management. | ||
* **Event Handling** - Event handling in ALS-Render is streamlined by automating the addition of event listeners. All event functions are stored in `component.actions` and are automatically attached to elements as listeners after each update. This setup allows for a more declarative attachment of events compared to manual management. | ||
@@ -18,3 +18,2 @@ * **Styles** - The `style` attribute in ALS-Render can be only a string | ||
* **Context** - ALS-Render provides a global `context` variable accessible by default in each component. This context can be used to share data and functions across components, simplifying the management of global states or configurations. | ||
* The context includes properties: components,actions,counter,update and more which should stay without modification | ||
@@ -26,8 +25,13 @@ * **Event Naming** - Events in ALS-Render follow the standard HTML naming conventions (e.g., `onclick` instead of `onClick`). This adherence simplifies the learning curve for those familiar with traditional HTML and JavaScript. | ||
* **No Lifecycle Hooks** - ALS-Render does not provide lifecycle hooks like those found in React (e.g., `useEffect`, `componentDidMount`). Developers need to manually manage setup, updates, and teardown of components, offering more control over these operations but requiring a more hands-on approach to managing component lifecycles. | ||
* **Lifecycle Hooks** - ALS-Render provides lifecycle hooks as mount and unmount. | ||
* inside component available `this.on('mount',(element) => {})` and `this.on('unmount',() => {})` | ||
* **onload** - ALS-Render has onload event for each element. | ||
* **updating** - Each component has `this.update(props,attributes,inner)` | ||
* **updating** - Each component has `this.update(props,inner)` | ||
* **context.link** - by using `context.link(href)` you can add link stylesheet to html | ||
* **context.style** - by using `context.style(cssStyles)` you can add css styles to html | ||
* **context.browser** - true inside browser and false in Nodejs | ||
* **context.ssr** - true if html rendered on NodeJS | ||
* **context.data** - includes data passed in render function |
@@ -6,7 +6,8 @@ const build = require('./build') | ||
await modules.getContent() | ||
const { resultFn, context } = build(modules, contextName) | ||
const { resultFn, context, add } = build(modules, contextName,true,false) | ||
context.data = data | ||
if (selector) { | ||
const element = document.querySelector(selector) | ||
if(element) { | ||
element.innerHTML = resultFn(data).trim() | ||
if (element) { | ||
element.innerHTML = add + resultFn(data).trim() | ||
context.runActions() | ||
@@ -13,0 +14,0 @@ } |
@@ -6,11 +6,9 @@ const getFunction = require('./get-function') | ||
if(originalFn === null) return content | ||
const newFunction = /*js*/`function ${componentName}(props={},attributes=[],inner,component) { | ||
const newFunction = /*js*/`function ${componentName}(props={},inner) { | ||
let originalFn = ${originalFn} | ||
component = context.component('${componentName}',props,attributes,inner,component) | ||
const component = context.component('${componentName}',props,inner) | ||
originalFn = originalFn.bind(component) | ||
const result = originalFn(props,inner) | ||
context.hashes[component.name] = context.genHash(result) | ||
return result | ||
return component.genHash(originalFn(props,inner)) | ||
} | ||
context.components.${componentName} = ${componentName} | ||
context.addComponentFn('${componentName}',${componentName}) | ||
` | ||
@@ -17,0 +15,0 @@ return content.replace(originalFn,newFunction) |
function buildAction(toAdd,element) { | ||
let [name,value] = toAdd | ||
const event = name.split('on')[1].toLowerCase() | ||
const fn = value | ||
value = '${context.counter++}' | ||
const selector = `[${event}="\${context.counter-1}"]` | ||
const addToActions = '$'+`{context.actions.push({name:this.name,selector:\`${selector}\`,event:'${event}',fn:${fn}}) ? '' : ''}` | ||
value = value + addToActions | ||
value = '$'+`{this.addAction('${event}',${value})}` | ||
element.attributes.push([event,value]) | ||
@@ -10,0 +6,0 @@ } |
@@ -1,15 +0,17 @@ | ||
const singleProps = ['checked', 'disabled', 'selected'] | ||
const singleProps = ["disabled", "checked", "readonly", "required", "hidden", "autofocus", "multiple", "selected", "controls", "loop", "muted", "open", "spellcheck", "draggable", "contenteditable", "novalidate"]; | ||
const buildAction = require('./build-action') | ||
function buildProp(toAdd, to, element) { | ||
if(!toAdd) return | ||
if (!toAdd) return | ||
let [name, value] = toAdd | ||
if(name === 'className') name = 'class' | ||
if(singleProps.includes(name)) { | ||
if (name === 'className') name = 'class' | ||
name = name.toLowerCase() | ||
if (singleProps.includes(name)) { | ||
name = `\${${value} ? '${name}' : ''}` | ||
value = undefined | ||
element.attributes.push([name]) | ||
} else if(to === 'props' && name.startsWith('on')) buildAction(toAdd,element) | ||
else element[to].push([name,value]) | ||
} else if (to === 'props' && name.startsWith('on')) buildAction(toAdd, element) | ||
else element[to].push([name, value]) | ||
} | ||
module.exports = buildProp |
@@ -9,3 +9,3 @@ const getAttributes = require('./attributes/get-attributes') | ||
if(content[i + 1] === '>') { | ||
this.isComponent = true | ||
this.isComponent = false | ||
this.tagName = '' | ||
@@ -21,3 +21,3 @@ this.i = i+2 | ||
if(componentName) { | ||
this.attributes.push(['component',`\${'${componentName}'+ (props.key !== undefined ? props.key : \'\')}`]) | ||
this.attributes.push(['component',`\${this.name}`]) | ||
} | ||
@@ -27,2 +27,3 @@ this.i = getAttributes(content[i], i, this, content) | ||
if (this.selfClosed === false) this.getInner(content) | ||
} | ||
@@ -29,0 +30,0 @@ |
@@ -5,5 +5,12 @@ function getOuter(element) { | ||
if(isComponent) { | ||
props = '{'+props.map(([name,value]) => `${name}:${value}`).join(',')+ (rest ? ','+rest : '') + '}' | ||
outer = '${'+`${tagName}(${props},${JSON.stringify(attributes)},\`${inner}\`)`+'}' | ||
let vars = [...props,...attributes.map(([k,v]) => ([k,'"'+v+'"']))] | ||
.map(([name,value]) => `${name}:${value}`) | ||
if(rest) vars.push(rest) | ||
props = '{'+ vars.join(',') + '}' | ||
outer = '${'+`${tagName}(${props},\`${inner}\`)`+'}' | ||
} else { | ||
if(tagName === '') { | ||
if(inner) return inner | ||
return '' | ||
} | ||
props = props.map(([name,value]) => ([name,'${'+value+'}'])) | ||
@@ -10,0 +17,0 @@ const atts = [...attributes,...props].map(([name,value]) => { |
const buildContent = require('./build-content/index') | ||
const Context = require('./context') | ||
const Context = require('./context/context') | ||
function build(app,contextName='context') { | ||
const context = new Context() | ||
function build(app, contextName = 'context',browser,ssr) { | ||
const context = new Context(browser,ssr) | ||
for (const path in app.contents) { | ||
app.contents[path] = buildContent(app.contents[path],path) | ||
app.contents[path] = buildContent(app.contents[path], path) | ||
} | ||
const resultFn = app.build({}, context, contextName) | ||
return {resultFn,context} | ||
let add = '' | ||
let { links, styles } = context | ||
if((ssr && !browser) || (!ssr && browser)) { | ||
add = [ | ||
...links.map(link => /*html*/`<link rel="stylesheet" href="${link}">`), | ||
styles.length ? /*html*/`<style>${styles.join('\n')}</style>` : '' | ||
].filter(Boolean).join('\n') + '\n' | ||
} | ||
return { resultFn, context, add } | ||
} | ||
module.exports = build |
@@ -14,3 +14,5 @@ function buildFn(fnBody,path) { | ||
function require(path) { return modules[path] || null }; | ||
const ${contextName} = new Context(); | ||
const ${contextName} = new Context(true,true); | ||
${contextName}.data = data | ||
context.rendered = true | ||
${keys.map((path, i) => buildFn(contents[path],path)).join('\n')} | ||
@@ -17,0 +19,0 @@ modules['${keys[keys.length-1]}'](data) |
@@ -5,6 +5,7 @@ const build = require('./build') | ||
const UglifyJS = require("uglify-js"); | ||
const Context = require('./context') | ||
const { stringContext, minifiedStringContext } = require('./context/stringContext') | ||
class Render { | ||
static stringContext = Context.toString() | ||
static minifiedStringContext = UglifyJS.minify(Context.toString()).code | ||
static stringContext = stringContext | ||
static minifiedStringContext = minifiedStringContext | ||
static contextName = 'context'; | ||
@@ -14,7 +15,7 @@ static minified = false; | ||
static cache = {}; | ||
static render(path,data,minified) { | ||
return new Render(path,minified).build(data) | ||
static render(path, data, minified) { | ||
return new Render(path, minified).build(data) | ||
} | ||
constructor(path,minified = Render.minified) { | ||
if(Render.cache[path]) { | ||
constructor(path, minified = Render.minified) { | ||
if (Render.cache[path]) { | ||
const obj = Render.cache[path]; | ||
@@ -28,14 +29,15 @@ obj.minified = minified | ||
} | ||
getContent(path) { | ||
const modules = new Require(path) | ||
modules.getContent() | ||
const { resultFn, context } = build(modules,Render.contextName) | ||
const { resultFn, context, add } = build(modules, Render.contextName, false,true) | ||
this.add = add | ||
this.resultFn = resultFn | ||
this.bundleFn = getBundle(modules,Render) | ||
if(this.minified) this.bundleFn = UglifyJS.minify(this.bundleFn).code | ||
this.bundleFn = getBundle(modules, Render) | ||
if (this.minified) this.bundleFn = UglifyJS.minify(this.bundleFn).code | ||
} | ||
build(data={}) { | ||
this.rawHtml = this.resultFn(data).trim() | ||
build(data = {}) { | ||
this.rawHtml = this.add + this.resultFn(data).trim() | ||
this.callBundle = `${Render.bundleName}(${JSON.stringify(data)})` | ||
@@ -46,3 +48,3 @@ return this | ||
get bundle() { | ||
return '\n'+[this.context,this.bundleFn,this.callBundle].join('\n') | ||
return '\n' + [this.context, this.bundleFn, this.callBundle].join('\n') | ||
} | ||
@@ -49,0 +51,0 @@ get context() { |
{ | ||
"name": "als-render", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"main": "index.js", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -32,3 +32,3 @@ # ALS-Render | ||
const app = express() | ||
const render = require('als-render'); // Path to the ALS-Render library | ||
const Render = require('als-render'); | ||
@@ -50,3 +50,3 @@ const layout = (rawHtml,bundle) => `<!DOCTYPE html> | ||
const path = './path/to/your/JSXComponent'; | ||
const contextName = 'context'; | ||
Render.contextName = 'context'; | ||
const minified = false; | ||
@@ -56,3 +56,3 @@ | ||
const data = {}; | ||
const { rawHtml, bundle } = render(path,data,contextName,minified); | ||
const { rawHtml, bundle } = Render.render(path,data,minified); | ||
res.end(layout(rawHtml, bundle)) | ||
@@ -102,3 +102,3 @@ }) | ||
* **Event Handling** - Event handling in ALS-Render is streamlined by automating the addition of event listeners. All event functions are stored in `context.actions` and are automatically attached to elements as listeners after each update. This setup allows for a more declarative attachment of events compared to manual management. | ||
* **Event Handling** - Event handling in ALS-Render is streamlined by automating the addition of event listeners. All event functions are stored in `component.actions` and are automatically attached to elements as listeners after each update. This setup allows for a more declarative attachment of events compared to manual management. | ||
@@ -110,3 +110,2 @@ * **Styles** - The `style` attribute in ALS-Render can be only a string | ||
* **Context** - ALS-Render provides a global `context` variable accessible by default in each component. This context can be used to share data and functions across components, simplifying the management of global states or configurations. | ||
* The context includes properties: components,actions,counter,update and more which should stay without modification | ||
@@ -118,9 +117,14 @@ * **Event Naming** - Events in ALS-Render follow the standard HTML naming conventions (e.g., `onclick` instead of `onClick`). This adherence simplifies the learning curve for those familiar with traditional HTML and JavaScript. | ||
* **No Lifecycle Hooks** - ALS-Render does not provide lifecycle hooks like those found in React (e.g., `useEffect`, `componentDidMount`). Developers need to manually manage setup, updates, and teardown of components, offering more control over these operations but requiring a more hands-on approach to managing component lifecycles. | ||
* **Lifecycle Hooks** - ALS-Render provides lifecycle hooks as mount and unmount. | ||
* inside component available `this.on('mount',(element) => {})` and `this.on('unmount',() => {})` | ||
* **onload** - ALS-Render has onload event for each element. | ||
* **updating** - Each component has `this.update(props,attributes,inner)` | ||
* **updating** - Each component has `this.update(props,inner)` | ||
* **context.link** - by using `context.link(href)` you can add link stylesheet to html | ||
* **context.style** - by using `context.style(cssStyles)` you can add css styles to html | ||
* **context.browser** - true inside browser and false in Nodejs | ||
* **context.ssr** - true if html rendered on NodeJS | ||
* **context.data** - includes data passed in render function | ||
@@ -127,0 +131,0 @@ ## Counter example |
309
render.js
@@ -187,146 +187,131 @@ const Require = (function(){ | ||
function require(path) { return modules[path] || null }; | ||
modules['/lib/context.js'] = (function (){ | ||
modules['/lib/context/component.js'] = (function (){ | ||
const module = { exports: {} } | ||
const exports = module.exports | ||
class Context { | ||
Component = class Component { | ||
static context | ||
constructor(componentName, props, attributes, inner) { | ||
this.componentName = componentName | ||
this.props = props | ||
this.attributes = attributes | ||
this.inner = inner | ||
} | ||
class Component { | ||
static fns = {} | ||
static components = {} | ||
static componentsToUpdate = {} | ||
static genHash(content) { | ||
const encoder = new TextEncoder(); | ||
const data = encoder.encode(content); | ||
let hash = 0, cur = 0; | ||
const fns = [n => { hash += n; return 1 }, n => { hash -= n; return 0 }] | ||
for (let b of data) { cur = fns[cur](b) } | ||
return hash | ||
} | ||
updateData(attributes, props, inner) { | ||
if (attributes !== this.attributes) this.attributes = attributes; | ||
if (props !== this.props) this.props = props | ||
if (inner !== this.inner) this.inner = inner | ||
} | ||
createNewElement(rawHtml) { | ||
const template = document.createElement('template'); | ||
template.innerHTML = rawHtml.trim(); | ||
return template.content.firstChild; | ||
constructor(componentName, props = {},inner) { | ||
const { key = '' } = props | ||
const name = componentName + key | ||
if (Component.components[name]) { | ||
const component = Component.components[name] | ||
component.init(props,inner) | ||
return component | ||
} | ||
Component.components[name] = this | ||
this.mounted = false | ||
this.name = name | ||
this.selector = `[component=${this.name}]` | ||
this.fn = Component.fns[componentName] | ||
this.init() | ||
} | ||
init(props,inner) { | ||
this.actions = [] | ||
this.counter = 0; | ||
this.props = props | ||
this.inner = inner | ||
this.hooks = { mount: [() => this.mounted = true], unmount: [] } | ||
} | ||
updateComponent(oldElement, newElement) { | ||
const updatedComponent = oldElement | ||
function cloneTag(element) { | ||
const { attributes, tagName } = element | ||
const newElement = document.createElement(tagName) | ||
for (const { name, value } of attributes) { | ||
newElement.setAttribute(name, value) | ||
} | ||
return newElement | ||
} | ||
function updateDom(oldNode, newNode) { | ||
const oldChildren = new Set(Array.from(oldNode.childNodes)); | ||
const newChildren = Array.from(newNode.childNodes); | ||
newChildren.forEach((element, i) => { | ||
if (element.nodeType === Node.TEXT_NODE) oldNode.appendChild(element); | ||
else if (element.getAttribute('component')) { | ||
const name = element.getAttribute('component') | ||
const hash = Component.context.hashes[name] | ||
const oldComponent = updatedComponent.querySelector(`[component=${name}]`) | ||
if (oldComponent && oldComponent.hash === hash) { | ||
oldNode.appendChild(oldComponent) | ||
if (oldChildren.has(oldComponent)) oldChildren.delete(oldComponent) | ||
Component.context.excludes.push(name) | ||
} else oldNode.appendChild(element) | ||
} else { | ||
const childOldNode = cloneTag(element) | ||
oldNode.appendChild(childOldNode) | ||
updateDom(childOldNode, element) | ||
} | ||
}); | ||
oldChildren.forEach(element => element.remove()); | ||
} | ||
oldElement.style.display = 'none' | ||
updateDom(oldElement, newElement) | ||
oldElement.style.display = '' | ||
} | ||
addAction(event, fn) { | ||
const id = this.name + this.counter++ | ||
this.actions.push({ event, id, fn }) | ||
return id | ||
} | ||
update(props = this.props, attributes = this.attributes, inner = this.inner) { | ||
this.updateData(attributes, props, inner) | ||
const selector = `[component=${this.name}]` | ||
on(event, fn) { | ||
if (!this.hooks[event]) return | ||
this.hooks[event].push(fn) | ||
} | ||
update(props=this.props,inner=this.inner) { | ||
this.props = props | ||
this.inner = inner | ||
const element = document.querySelector(this.selector) | ||
if(!element || !this.fn) return | ||
const newHtml = this.fn(props, inner, this) | ||
if(this.hash === this.oldHash) return | ||
element.outerHTML = newHtml | ||
Component.context.runActions() | ||
} | ||
genHash(content) { | ||
const hash = Component.genHash(content+this.name) | ||
this.oldHash = this.hash | ||
Component.componentsToUpdate[this.name] = this | ||
this.hash = hash | ||
return content | ||
} | ||
} | ||
module.exports = Component | ||
return module.exports; | ||
})(); | ||
modules['/lib/context/context.js'] = (function (){ | ||
const module = { exports: {} } | ||
const exports = module.exports | ||
const Component = require('/lib/context/component.js') | ||
class Context { | ||
links = []; styles = []; counter=0; | ||
constructor(browser = true,ssr=false) { | ||
Component.context = this; | ||
this.browser = browser | ||
this.ssr = ssr | ||
} | ||
addComponentFn(name,fn) { Component.fns[name] = fn} | ||
component(componentName, props, inner) { return new Component(componentName,props,inner) } | ||
style(styles) { this.styles.push(styles) } | ||
runActions() { | ||
for (const name in Component.componentsToUpdate) { | ||
const component = Component.componentsToUpdate[name] | ||
const { actions, hooks, selector } = component | ||
const element = document.querySelector(selector) | ||
const componentFn = Component.context.components[this.componentName] | ||
if (element && componentFn) { | ||
const newHtml = componentFn(props, attributes, inner, this) | ||
const newElement = this.createNewElement(newHtml) | ||
if(newHtml.length < 1000) element.replaceWith(newElement) | ||
else this.updateComponent(element, newElement) | ||
Component.context.runActions() | ||
this.element = element | ||
} | ||
const parent = element || document | ||
hooks.mount.forEach(fn => fn(element)) | ||
actions.forEach(({ event, fn, id }) => { | ||
const elementForEvent = parent.querySelector(`[${event}="${id}"]`) | ||
if (!elementForEvent) return | ||
if (event === 'load') fn(elementForEvent) | ||
else elementForEvent.addEventListener(event, fn) | ||
}) | ||
component.actions = [] | ||
hooks.mount = [] | ||
} | ||
get name() { | ||
const key = this.props.key !== undefined ? this.props.key : '' | ||
return `${this.componentName}${key}` | ||
for (const name in Component.components) { | ||
const { selector, hooks } = Component.components[name] | ||
if (document.querySelector(selector)) continue | ||
hooks.unmount.forEach(fn => fn()); | ||
delete Component.components[name] | ||
} | ||
} | ||
constructor() { | ||
this.Component.context = this | ||
this.counter = 0; | ||
this.components = {}; | ||
this.reset() | ||
} | ||
reset() { | ||
this.actions = []; | ||
this.hashes = {}; | ||
this.excludes = []; | ||
this.links = []; | ||
Component.componentsToUpdate = {} | ||
} | ||
component(componentName, props, attributes, inner, component) { | ||
if (component) component.updateData(attributes, props, inner) | ||
else component = new this.Component(componentName, props, attributes, inner) | ||
return component | ||
} | ||
link(link) { | ||
const arr = this.currentPath.split('/') | ||
arr.pop() | ||
const arr = this.currentPath.split('/'); | ||
arr.pop(); | ||
link.split('/').forEach(part => { | ||
if(part === '.') return | ||
if(part === '..') arr.pop() | ||
else arr.push(part) | ||
if (part === '..') arr.pop() | ||
else if (part !== '.') arr.push(part) | ||
}) | ||
link = arr.join('/') | ||
this.links.push(link) | ||
this.links.push(arr.join('/')) | ||
} | ||
genHash(content) { | ||
const encoder = new TextEncoder(); | ||
const data = encoder.encode(content); | ||
let hash = 0, cur = 0; | ||
const fns = [n => { hash += n; return 1 }, n => { hash -= n; return 0 }] | ||
for (let b of data) { cur = fns[cur](b) } | ||
return hash | ||
} | ||
runActions() { | ||
this.links.forEach(link => { | ||
document.querySelector('head').insertAdjacentHTML('afterend',/*html*/`<link rel="stylesheet" href="${link}">`) | ||
}); | ||
this.actions.forEach(({ name, event, fn, selector }) => { | ||
if (this.excludes.includes(name)) return | ||
const element = document.querySelector(selector); | ||
if (!element) return | ||
if (event === 'load') fn(element) | ||
else element.addEventListener(event, fn) | ||
}) | ||
for (const name in this.hashes) { | ||
const element = document.querySelector(`[component=${name}]`) | ||
if (element) element.hash = this.hashes[name] | ||
} | ||
this.reset() | ||
} | ||
} | ||
@@ -389,11 +374,9 @@ | ||
if(originalFn === null) return content | ||
const newFunction = /*js*/`function ${componentName}(props={},attributes=[],inner,component) { | ||
const newFunction = /*js*/`function ${componentName}(props={},inner) { | ||
let originalFn = ${originalFn} | ||
component = context.component('${componentName}',props,attributes,inner,component) | ||
const component = context.component('${componentName}',props,inner) | ||
originalFn = originalFn.bind(component) | ||
const result = originalFn(props,inner) | ||
context.hashes[component.name] = context.genHash(result) | ||
return result | ||
return component.genHash(originalFn(props,inner)) | ||
} | ||
context.components.${componentName} = ${componentName} | ||
context.addComponentFn('${componentName}',${componentName}) | ||
` | ||
@@ -413,5 +396,12 @@ return content.replace(originalFn,newFunction) | ||
if(isComponent) { | ||
props = '{'+props.map(([name,value]) => `${name}:${value}`).join(',')+ (rest ? ','+rest : '') + '}' | ||
outer = '${'+`${tagName}(${props},${JSON.stringify(attributes)},\`${inner}\`)`+'}' | ||
let vars = [...props,...attributes.map(([k,v]) => ([k,'"'+v+'"']))] | ||
.map(([name,value]) => `${name}:${value}`) | ||
if(rest) vars.push(rest) | ||
props = '{'+ vars.join(',') + '}' | ||
outer = '${'+`${tagName}(${props},\`${inner}\`)`+'}' | ||
} else { | ||
if(tagName === '') { | ||
if(inner) return inner | ||
return '' | ||
} | ||
props = props.map(([name,value]) => ([name,'${'+value+'}'])) | ||
@@ -461,7 +451,3 @@ const atts = [...attributes,...props].map(([name,value]) => { | ||
const event = name.split('on')[1].toLowerCase() | ||
const fn = value | ||
value = '${context.counter++}' | ||
const selector = `[${event}="\${context.counter-1}"]` | ||
const addToActions = '$'+`{context.actions.push({name:this.name,selector:\`${selector}\`,event:'${event}',fn:${fn}}) ? '' : ''}` | ||
value = value + addToActions | ||
value = '$'+`{this.addAction('${event}',${value})}` | ||
element.attributes.push([event,value]) | ||
@@ -476,14 +462,16 @@ } | ||
const exports = module.exports | ||
const singleProps = ['checked', 'disabled', 'selected'] | ||
const singleProps = ["disabled", "checked", "readonly", "required", "hidden", "autofocus", "multiple", "selected", "controls", "loop", "muted", "open", "spellcheck", "draggable", "contenteditable", "novalidate"]; | ||
const buildAction = require('/lib/build-content/jsx/attributes/build-action.js') | ||
function buildProp(toAdd, to, element) { | ||
if(!toAdd) return | ||
if (!toAdd) return | ||
let [name, value] = toAdd | ||
if(name === 'className') name = 'class' | ||
if(singleProps.includes(name)) { | ||
if (name === 'className') name = 'class' | ||
name = name.toLowerCase() | ||
if (singleProps.includes(name)) { | ||
name = `\${${value} ? '${name}' : ''}` | ||
value = undefined | ||
element.attributes.push([name]) | ||
} else if(to === 'props' && name.startsWith('on')) buildAction(toAdd,element) | ||
else element[to].push([name,value]) | ||
} else if (to === 'props' && name.startsWith('on')) buildAction(toAdd, element) | ||
else element[to].push([name, value]) | ||
} | ||
@@ -577,3 +565,3 @@ | ||
if(content[i + 1] === '>') { | ||
this.isComponent = true | ||
this.isComponent = false | ||
this.tagName = '' | ||
@@ -589,3 +577,3 @@ this.i = i+2 | ||
if(componentName) { | ||
this.attributes.push(['component',`\${'${componentName}'+ (props.key !== undefined ? props.key : \'\')}`]) | ||
this.attributes.push(['component',`\${this.name}`]) | ||
} | ||
@@ -595,2 +583,3 @@ this.i = getAttributes(content[i], i, this, content) | ||
if (this.selfClosed === false) this.getInner(content) | ||
} | ||
@@ -691,11 +680,20 @@ | ||
const buildContent = require('/lib/build-content/index.js') | ||
const Context = require('/lib/context.js') | ||
const Context = require('/lib/context/context.js') | ||
function build(app,contextName='context') { | ||
const context = new Context() | ||
function build(app, contextName = 'context',browser,ssr) { | ||
const context = new Context(browser,ssr) | ||
for (const path in app.contents) { | ||
app.contents[path] = buildContent(app.contents[path],path) | ||
app.contents[path] = buildContent(app.contents[path], path) | ||
} | ||
const resultFn = app.build({}, context, contextName) | ||
return {resultFn,context} | ||
let add = '' | ||
let { links, styles } = context | ||
if((ssr && !browser) || (!ssr && browser)) { | ||
add = [ | ||
...links.map(link => /*html*/`<link rel="stylesheet" href="${link}">`), | ||
styles.length ? /*html*/`<style>${styles.join('\n')}</style>` : '' | ||
].filter(Boolean).join('\n') + '\n' | ||
} | ||
return { resultFn, context, add } | ||
} | ||
@@ -714,7 +712,8 @@ | ||
await modules.getContent() | ||
const { resultFn, context } = build(modules, contextName) | ||
const { resultFn, context, add } = build(modules, contextName,true,false) | ||
context.data = data | ||
if (selector) { | ||
const element = document.querySelector(selector) | ||
if(element) { | ||
element.innerHTML = resultFn(data).trim() | ||
if (element) { | ||
element.innerHTML = add + resultFn(data).trim() | ||
context.runActions() | ||
@@ -738,4 +737,4 @@ } | ||
} catch(error) { | ||
parseError(error, {"/lib/context.js":{"from":4,"to":152},"/lib/build-content/remove-comments.js":{"from":153,"to":164},"/lib/build-content/build-component/get-function.js":{"from":165,"to":196},"/lib/build-content/build-component/index.js":{"from":197,"to":220},"/lib/build-content/jsx/outer.js":{"from":221,"to":243},"/lib/build-content/jsx/breckets.js":{"from":244,"to":268},"/lib/build-content/jsx/attributes/build-action.js":{"from":269,"to":285},"/lib/build-content/jsx/attributes/build-prop.js":{"from":286,"to":305},"/lib/build-content/jsx/attributes/get-attributes.js":{"from":306,"to":377},"/lib/build-content/jsx/element.js":{"from":378,"to":450},"/lib/build-content/jsx/jsx-parser.js":{"from":451,"to":478},"/lib/build-content/index.js":{"from":479,"to":495},"/lib/build.js":{"from":496,"to":513},"/lib/browser.js":{"from":514,"to":536}}, 536) | ||
parseError(error, {"/lib/context/component.js":{"from":4,"to":78},"/lib/context/context.js":{"from":79,"to":137},"/lib/build-content/remove-comments.js":{"from":138,"to":149},"/lib/build-content/build-component/get-function.js":{"from":150,"to":181},"/lib/build-content/build-component/index.js":{"from":182,"to":203},"/lib/build-content/jsx/outer.js":{"from":204,"to":233},"/lib/build-content/jsx/breckets.js":{"from":234,"to":258},"/lib/build-content/jsx/attributes/build-action.js":{"from":259,"to":271},"/lib/build-content/jsx/attributes/build-prop.js":{"from":272,"to":293},"/lib/build-content/jsx/attributes/get-attributes.js":{"from":294,"to":365},"/lib/build-content/jsx/element.js":{"from":366,"to":439},"/lib/build-content/jsx/jsx-parser.js":{"from":440,"to":467},"/lib/build-content/index.js":{"from":468,"to":484},"/lib/build.js":{"from":485,"to":511},"/lib/browser.js":{"from":512,"to":535}}, 535) | ||
} | ||
})() |
@@ -9,12 +9,10 @@ let Require=function(){function i(t,e){if(o.contents[t]&&o.contents[t].children.includes(e))throw`cyclic dependency between ${e} and `+t}function c(t,e){var n,t=t.split("/"),r=[];for(n of[...e.split("/").slice(0,-1),...t])".."===n?0<r.length&&".."!==r[r.length-1]?r.pop():r.push(n):"."!==n&&r.push(n);e=r.join("/");return e.endsWith(".js")?e:e+".js"}async function u(t,e="text"){t=await fetch(t);if(t.ok)return t[e]();throw new Error("HTTP error! status: "+t.status)}async function t({contents:r,fullPath:t},a){let o=async s=>{if(void 0===r[s]){if(!a.contents[s]){let t=await u(s),r=[],o=[];t=t.replace(/^(?!\/\/|\/\*.*\*\/).*require\(["'`](.*)["'`]\)/gm,(t,e)=>{var n;return e.startsWith(".")?(i(n=c(e,s),s),r.push(n),t.replace(e,n)):(o.push({match:t,modulePath:e}),t)}),t=await async function(t,e,n){if(0!==t.length)for(var{match:r,modulePath:o}of t){var s=`/node_modules/${o}/package.json`;(await fetch(s,{method:"HEAD"})).ok?({main:s="index.js"}=await u(s,"json"),i(s=`/node_modules/${o}/`+s,o),e.push(s),n=n.replace(r,r.replace(o,s))):console.warn(`The module "${o}" can't be imported and will be replaced with null`)}return n}(o,r,t),a.contents[s]={content:t,children:r}}let{content:t,children:e}=a.contents[s];r[s]=t;for(var n of e)await o(n)}};await o(t)}class o{static contents={};static async getModule(t,e,n,r){t=new o(t);return await t.getContent(),t.build(r,e,n)}constructor(t){this.contents={},this.path=t,this.fullPath=c(t,location.pathname),this.contentReady=!1}async getContent(){return this.contentReady||(await t(this,o),this.keys=function(e,n){let r=new Set,o=t=>{t.forEach(t=>{e[t]&&(o(n.contents[t].children),r.add(t))})};return o(Object.keys(e).reverse()),Array.from(r)}(this.contents,o),this.contentReady=!0),this}build(t={},e={},r="context"){var{fn:r,modulesLines:o,curLastLine:s}=function(t="context",r){let o={},s=3;var e=r.keys.map((t,e)=>{let n=`modules['${t}'] = (function (){ | ||
`+e),modulesLines:o,curLastLine:s}}(r,this);try{return r(t,e)}catch(n){{r=n;var a=o;var i=s;let[t,...e]=r.stack.split("\n");throw e=e.map(t=>{var e=t.match(/<anonymous>:(\d*):(\d*)\)$/);if(e){let n=Number(e[1]);if(n+1!==i){var r,o,e=Number(e[2]),s=Object.entries(a).filter(([,{from:t,to:e}])=>n>=t&&n<=e);if(0!==s.length)return[s,{from:r,to:o}]=s[0],` at ${t.match(/at\s(.*?)\s/)[1]} ${s} (${n-r-2}:${e})`}}}).filter(Boolean),r.stack=t+"\n"+e.join("\n"),r;return}}}}return o}(),require=Require.getModule; | ||
const render = (()=>{function t(e,t){function n(t){return e[t]||null}var s;return e["/lib/context.js"]=(()=>{var t={exports:{}};class e{Component=class c{static context;constructor(t,e,n,s){this.componentName=t,this.props=e,this.attributes=n,this.inner=s}updateData(t,e,n){t!==this.attributes&&(this.attributes=t),e!==this.props&&(this.props=e),n!==this.inner&&(this.inner=n)}createNewElement(t){var e=document.createElement("template");return e.innerHTML=t.trim(),e.content.firstChild}updateComponent(t,e){let u=t;t.style.display="none",function i(o,t){let l=new Set(Array.from(o.childNodes));Array.from(t.childNodes).forEach((t,e)=>{var n,s,r;t.nodeType===Node.TEXT_NODE?o.appendChild(t):t.getAttribute("component")?(n=t.getAttribute("component"),r=c.context.hashes[n],(s=u.querySelector(`[component=${n}]`))&&s.hash===r?(o.appendChild(s),l.has(s)&&l.delete(s),c.context.excludes.push(n)):o.appendChild(t)):(r=(t=>{var e,n,{attributes:t,tagName:s}=t,r=document.createElement(s);for({name:e,value:n}of t)r.setAttribute(e,n);return r})(t),o.appendChild(r),i(r,t))}),l.forEach(t=>t.remove())}(t,e),t.style.display=""}update(t=this.props,e=this.attributes,n=this.inner){this.updateData(e,t,n);var s=`[component=${this.name}]`,s=document.querySelector(s),r=c.context.components[this.componentName];s&&r&&(r=r(t,e,n,this),t=this.createNewElement(r),r.length<1e3?s.replaceWith(t):this.updateComponent(s,t),c.context.runActions(),this.element=s)}get name(){var t=void 0!==this.props.key?this.props.key:"";return""+this.componentName+t}};constructor(){(this.Component.context=this).counter=0,this.components={},this.reset()}reset(){this.actions=[],this.hashes={},this.excludes=[],this.links=[]}component(t,e,n,s,r){return r?r.updateData(n,e,s):r=new this.Component(t,e,n,s),r}link(t){let e=this.currentPath.split("/");e.pop(),t.split("/").forEach(t=>{"."!==t&&(".."===t?e.pop():e.push(t))}),t=e.join("/"),this.links.push(t)}genHash(t){t=(new TextEncoder).encode(t);let e=0,n=0;var s,r=[t=>(e+=t,1),t=>(e-=t,0)];for(s of t)n=r[n](s);return e}runActions(){for(var t in this.links.forEach(t=>{document.querySelector("head").insertAdjacentHTML("afterend",`<link rel="stylesheet" href="${t}">`)}),this.actions.forEach(({name:t,event:e,fn:n,selector:s})=>{this.excludes.includes(t)||(t=document.querySelector(s))&&("load"===e?n(t):t.addEventListener(e,n))}),this.hashes){var e=document.querySelector(`[component=${t}]`);e&&(e.hash=this.hashes[t])}this.reset()}}return t.exports=e,t.exports})(),e["/lib/build-content/remove-comments.js"]=((s={exports:{}}).exports=function(t){return t.replace(/\/\/.*(?=\n|$)/g,"").replace(/\{?\/\*[\s\S]*?\*\/\}?/g,"")},s.exports),e["/lib/build-content/build-component/get-function.js"]=((s={exports:{}}).exports=function(s,t){var e="[\\s\\S]*?",t=new RegExp(`^\\s*?function\\s*?${t}\\s*?\\(${e}\\)${e}{`,"m");if(!(e=s.match(t)))return null;let r=0,i;for(let n=e.index+e[0].length;n<s.length;n++){let t=s[n],e=s[n-1];if(t.match(/["'`]/)&&"\\"!==e)for(;e=s[n],s[++n]!==t&&"\\"!==e;);if("{"===t)r++;else if("}"===t)if(0<r)r--;else if(0===r){i=n+1;break}}return s.slice(e.index,i)},s.exports),e["/lib/build-content/build-component/index.js"]=(()=>{var t={exports:{}};let s=n("/lib/build-content/build-component/get-function.js");return t.exports=function(t,e){var n=s(t,e);return null===n?t:t.replace(n,`function ${e}(props={},attributes=[],inner,component) { | ||
const render = (()=>{function t(e,t){function n(t){return e[t]||null}var s;return e["/lib/context/component.js"]=(()=>{var t={exports:{}};class i{static fns={};static components={};static componentsToUpdate={};static genHash(t){t=(new TextEncoder).encode(t);let e=0,n=0;var s,o=[t=>(e+=t,1),t=>(e-=t,0)];for(s of t)n=o[n](s);return e}constructor(t,e={},n){var s,{key:o=""}=e,o=t+o;if(i.components[o])return(s=i.components[o]).init(e,n),s;(i.components[o]=this).mounted=!1,this.name=o,this.selector=`[component=${this.name}]`,this.fn=i.fns[t],this.init()}init(t,e){this.actions=[],this.counter=0,this.props=t,this.inner=e,this.hooks={mount:[()=>this.mounted=!0],unmount:[]}}addAction(t,e){var n=this.name+this.counter++;return this.actions.push({event:t,id:n,fn:e}),n}on(t,e){this.hooks[t]&&this.hooks[t].push(e)}update(t=this.props,e=this.inner){this.props=t,this.inner=e;var n=document.querySelector(this.selector);n&&this.fn&&(t=this.fn(t,e,this),this.hash!==this.oldHash)&&(n.outerHTML=t,i.context.runActions())}genHash(t){var e=i.genHash(t+this.name);return this.oldHash=this.hash,(i.componentsToUpdate[this.name]=this).hash=e,t}}return t.exports=i,t.exports})(),e["/lib/context/context.js"]=(()=>{var t={exports:{}};let l=n("/lib/context/component.js");return t.exports=class{links=[];styles=[];counter=0;constructor(t=!0,e=!1){(l.context=this).browser=t,this.ssr=e}addComponentFn(t,e){l.fns[t]=e}component(t,e,n){return new l(t,e,n)}style(t){this.styles.push(t)}runActions(){for(var t in l.componentsToUpdate){var t=l.componentsToUpdate[t],{actions:n,hooks:o,selector:i}=t;let e=document.querySelector(i),s=e||document;o.mount.forEach(t=>t(e)),n.forEach(({event:t,fn:e,id:n})=>{n=s.querySelector(`[${t}="${n}"]`);n&&("load"===t?e(n):n.addEventListener(t,e))}),t.actions=[],o.mount=[]}for(var e in l.components){var{selector:s,hooks:r}=l.components[e];document.querySelector(s)||(r.unmount.forEach(t=>t()),delete l.components[e])}l.componentsToUpdate={}}link(t){let e=this.currentPath.split("/");e.pop(),t.split("/").forEach(t=>{".."===t?e.pop():"."!==t&&e.push(t)}),this.links.push(e.join("/"))}},t.exports})(),e["/lib/build-content/remove-comments.js"]=((s={exports:{}}).exports=function(t){return t.replace(/\/\/.*(?=\n|$)/g,"").replace(/\{?\/\*[\s\S]*?\*\/\}?/g,"")},s.exports),e["/lib/build-content/build-component/get-function.js"]=((s={exports:{}}).exports=function(s,t){var e="[\\s\\S]*?",t=new RegExp(`^\\s*?function\\s*?${t}\\s*?\\(${e}\\)${e}{`,"m");if(!(e=s.match(t)))return null;let o=0,i;for(let n=e.index+e[0].length;n<s.length;n++){let t=s[n],e=s[n-1];if(t.match(/["'`]/)&&"\\"!==e)for(;e=s[n],s[++n]!==t&&"\\"!==e;);if("{"===t)o++;else if("}"===t)if(0<o)o--;else if(0===o){i=n+1;break}}return s.slice(e.index,i)},s.exports),e["/lib/build-content/build-component/index.js"]=(()=>{var t={exports:{}};let s=n("/lib/build-content/build-component/get-function.js");return t.exports=function(t,e){var n=s(t,e);return null===n?t:t.replace(n,`function ${e}(props={},inner) { | ||
let originalFn = ${n} | ||
component = context.component('${e}',props,attributes,inner,component) | ||
const component = context.component('${e}',props,inner) | ||
originalFn = originalFn.bind(component) | ||
const result = originalFn(props,inner) | ||
context.hashes[component.name] = context.genHash(result) | ||
return result | ||
return component.genHash(originalFn(props,inner)) | ||
} | ||
context.components.${e} = ${e} | ||
`)},t.exports})(),e["/lib/build-content/jsx/outer.js"]=((s={exports:{}}).exports=function(t){let{tagName:e,selfClosed:n,attributes:s,props:r,isComponent:i,rest:o,inner:l}=t,u="";return i?(r="{"+r.map(([t,e])=>t+":"+e).join(",")+(o?","+o:"")+"}",u="${"+`${e}(${r},${JSON.stringify(s)},\`${l}\`)`+"}"):(r=r.map(([t,e])=>[t,"${"+e+"}"]),t=[...s,...r].map(([t,e])=>e?t+`="${e}"`:t).join(" "),u=`<${e}${t.length?" "+t:""}>`,n||(u+=l+`</${e}>`)),u},s.exports),e["/lib/build-content/jsx/breckets.js"]=((s={exports:{}}).exports=function(t,e){let n=0,s=null;for(;t<e.length;)if(t++,null===s&&/["'`]/.test(e[t]))s=e[t];else if(s)e[t]===s&&"\\"!==e[t-1]&&(s=null);else if("{"===e[t])n++;else if("}"===e[t]){if(!(0<n))break;n--}return++t},s.exports),e["/lib/build-content/jsx/attributes/build-action.js"]=((s={exports:{}}).exports=function(t,e){var[t,n]=t,t=t.split("on")[1].toLowerCase(),s=n,n="${context.counter++}";e.attributes.push([t,n+="$"+`{context.actions.push({name:this.name,selector:\`${`[${t}="\${context.counter-1}"]`}\`,event:'${t}',fn:${s}}) ? '' : ''}`])},s.exports),e["/lib/build-content/jsx/attributes/build-prop.js"]=(()=>{var t={exports:{}};let i=["checked","disabled","selected"],o=n("/lib/build-content/jsx/attributes/build-action.js");return t.exports=function(n,s,r){if(n){let[t,e]=n;"className"===t&&(t="class"),i.includes(t)?(t=`\${${e} ? '${t}' : ''}`,e=void 0,r.attributes.push([t])):"props"===s&&t.startsWith("on")?o(n,r):r[s].push([t,e])}},t.exports})(),e["/lib/build-content/jsx/attributes/get-attributes.js"]=(()=>{var t={exports:{}};let p=n("/lib/build-content/jsx/attributes/build-prop.js");return t.exports=function(t,e,s,r){let i="",o="",l=!0,u=!1,c,n=0;function a(t,e,n="attributes"){for(p(e,n,s),l=!0,u=!1,i="",o="",c=null;0===r[t].trim().length;)t++;return t}for(;">"!==t&&!(e>=r.length);){if(l)if("{"===t){for(;e<r.length&&"}"!==(t=r[++e]);)s.rest+=t;a(e)}else if("="===t||0===t.trim().length)0<i.length&&(l=!1,u=!0);else{if(">"===r[e+1]){"/"===t?s.selfClosed=!0:""!==i&&s.attributes.push([i+t]),e++;break}i+=t}else u&&(c?"{"===c?(o+=t,"{"===t?n++:"}"===t&&(0<n?n--:e=a(e,[i,o.slice(0,-1)],"props"))):"\\"!==r[e-1]&&t===c?e=a(e,[i,o]):o+=t:/["'`{]/.test(t)?c=t:/[a-zA-Z]/.test(t)&&(""!==i&&s.attributes.push([i]),l=!0,u=!1,i=t));">"===(t=r[++e])&&u&&(o+=t,t=r[++e])}return++e},t.exports})(),e["/lib/build-content/jsx/element.js"]=(()=>{var t={exports:{}};let s=n("/lib/build-content/jsx/attributes/get-attributes.js"),r=n("/lib/build-content/jsx/breckets.js"),e=n("/lib/build-content/jsx/outer.js");class i{tagName="";rest="";inner="";attributes=[];props=[];selfClosed=!1;constructor(t,e,n){if(">"===t[e+1])this.isComponent=!0,this.tagName="",this.i=e+2;else{for(this.isComponent=/[A-Z]/.test(t[e+1]);e<t.length&&!1!==/[A-Za-z0-9.]/.test(t[++e]);)this.tagName+=t[e];n&&this.attributes.push(["component",`\${'${n}'+ (props.key !== undefined ? props.key : '')}`]),this.i=s(t[e],e,this,t)}!1===this.selfClosed&&this.getInner(t)}get outer(){return e(this)}getInner(t){var e=`</${this.tagName}>`;let n=0;for(var s="</>"==e?"<>":"<"+this.tagName;this.i<t.length;){if(this.inner+=t[this.i],this.inner.endsWith(s)&&n++,this.inner.endsWith(e)){if(!(0<n)){this.inner=this.inner.slice(0,-e.length).trim();break}n--}this.i++}this.buildInner()}buildInner(){let e="";if(!(this.inner.trim().length<2)&&(this.inner.includes("<")||this.inner.includes("{"))){for(let t=0;t<this.inner.length;t++){var n,s;"<"===this.inner[t]?(n=new i(this.inner,t),e+=n.outer,t=n.i):"{"===this.inner[t]?(n=t,t=r(t,this.inner),s=this.inner.slice(n,t),e+="$"+i.jsxParser(s)):e+=this.inner[t]}this.inner=e}}}return t.exports=i,t.exports})(),e["/lib/build-content/jsx/jsx-parser.js"]=(()=>{var t={exports:{}};let o=n("/lib/build-content/jsx/element.js");function e(e,n){let s="";for(let t=0;t<e.length;t++)if("("===e[t]){var r=t;for(t++;0===e[t].trim().length;)t++;if("<"===e[t]){var i=new o(e,t,n);for(t=i.i,s+="`"+i.outer+"`";")"!==e[t];)t++}else s+=e.slice(r,t+1)}else s+=e[t];return s}return o.jsxParser=e,t.exports=e,t.exports})(),e["/lib/build-content/index.js"]=(()=>{var t={exports:{}};let s=n("/lib/build-content/jsx/jsx-parser.js"),r=n("/lib/build-content/build-component/index.js"),i=n("/lib/build-content/remove-comments.js");return t.exports=function(t,e){var n=e.split("/").pop().replace(/\.js$/,""),e=`context.currentPath = '${e}'; | ||
`+s(i(t),n);return r(e,n)},t.exports})(),e["/lib/build.js"]=(()=>{var t={exports:{}};let r=n("/lib/build-content/index.js"),i=n("/lib/context.js");return t.exports=function(t,e="context"){var n,s=new i;for(n in t.contents)t.contents[n]=r(t.contents[n],n);return{resultFn:t.build({},s,e),context:s}},t.exports})(),e["/lib/browser.js"]=(()=>{var t={exports:{}};let r=n("/lib/build.js");return t.exports=async function(t,e="body",n={},s){await(t=new Require(t)).getContent();var{resultFn:t,context:s}=r(t,s);return e&&(e=document.querySelector(e))&&(e.innerHTML=t(n).trim(),s.runActions()),s},t.exports})(),e["/lib/browser.js"]}{var e;let s=new Function("return "+"{}")();(function t(e){for(var n in e)"function"==typeof e[n]&&e[n].name===Obj.recursiveName?e[n]=e[n](s):null!==e[n]&&"object"==typeof e[n]&&t(e[n])})(s),s}try{t({})}catch(n){{var s=n;var o={"/lib/context.js":{from:4,to:152},"/lib/build-content/remove-comments.js":{from:153,to:164},"/lib/build-content/build-component/get-function.js":{from:165,to:196},"/lib/build-content/build-component/index.js":{from:197,to:220},"/lib/build-content/jsx/outer.js":{from:221,to:243},"/lib/build-content/jsx/breckets.js":{from:244,to:268},"/lib/build-content/jsx/attributes/build-action.js":{from:269,to:285},"/lib/build-content/jsx/attributes/build-prop.js":{from:286,to:305},"/lib/build-content/jsx/attributes/get-attributes.js":{from:306,to:377},"/lib/build-content/jsx/element.js":{from:378,to:450},"/lib/build-content/jsx/jsx-parser.js":{from:451,to:478},"/lib/build-content/index.js":{from:479,to:495},"/lib/build.js":{from:496,to:513},"/lib/browser.js":{from:514,to:536}};var l=536;let[t,...e]=s.stack.split("\n");throw e=e.map(t=>{var e=t.match(/<anonymous>:(\d*):(\d*)\)$/);if(e){let n=Number(e[1]);if(n+1!==l){var s,r,e=Number(e[2]),i=Object.entries(o).filter(([,{from:t,to:e}])=>n>=t&&n<=e);if(0!==i.length)return[i,{from:s,to:r}]=i[0],` at ${t.match(/at\s(.*?)\s/)[1]} ${i} (${n-s-2}:${e})`}}}).filter(Boolean),s.stack=t+"\n"+e.join("\n"),s;return}}})(); | ||
context.addComponentFn('${e}',${e}) | ||
`)},t.exports})(),e["/lib/build-content/jsx/outer.js"]=((s={exports:{}}).exports=function(t){let{tagName:e,selfClosed:n,attributes:s,props:o,isComponent:i,rest:r,inner:l}=t,u="";if(i){t=[...o,...s.map(([t,e])=>[t,'"'+e+'"'])].map(([t,e])=>t+":"+e);r&&t.push(r),o="{"+t.join(",")+"}",u="${"+`${e}(${o},\`${l}\`)`+"}"}else{if(""===e)return l||"";o=o.map(([t,e])=>[t,"${"+e+"}"]);t=[...s,...o].map(([t,e])=>e?t+`="${e}"`:t).join(" ");u=`<${e}${t.length?" "+t:""}>`,n||(u+=l+`</${e}>`)}return u},s.exports),e["/lib/build-content/jsx/breckets.js"]=((s={exports:{}}).exports=function(t,e){let n=0,s=null;for(;t<e.length;)if(t++,null===s&&/["'`]/.test(e[t]))s=e[t];else if(s)e[t]===s&&"\\"!==e[t-1]&&(s=null);else if("{"===e[t])n++;else if("}"===e[t]){if(!(0<n))break;n--}return++t},s.exports),e["/lib/build-content/jsx/attributes/build-action.js"]=((s={exports:{}}).exports=function(t,e){var[t,n]=t,t=t.split("on")[1].toLowerCase();e.attributes.push([t,"$"+`{this.addAction('${t}',${n})}`])},s.exports),e["/lib/build-content/jsx/attributes/build-prop.js"]=(()=>{var t={exports:{}};let i=["disabled","checked","readonly","required","hidden","autofocus","multiple","selected","controls","loop","muted","open","spellcheck","draggable","contenteditable","novalidate"],r=n("/lib/build-content/jsx/attributes/build-action.js");return t.exports=function(n,s,o){if(n){let[t,e]=n;t=(t="className"===t?"class":t).toLowerCase(),i.includes(t)?(t=`\${${e} ? '${t}' : ''}`,e=void 0,o.attributes.push([t])):"props"===s&&t.startsWith("on")?r(n,o):o[s].push([t,e])}},t.exports})(),e["/lib/build-content/jsx/attributes/get-attributes.js"]=(()=>{var t={exports:{}};let p=n("/lib/build-content/jsx/attributes/build-prop.js");return t.exports=function(t,e,s,o){let i="",r="",l=!0,u=!1,c,n=0;function a(t,e,n="attributes"){for(p(e,n,s),l=!0,u=!1,i="",r="",c=null;0===o[t].trim().length;)t++;return t}for(;">"!==t&&!(e>=o.length);){if(l)if("{"===t){for(;e<o.length&&"}"!==(t=o[++e]);)s.rest+=t;a(e)}else if("="===t||0===t.trim().length)0<i.length&&(l=!1,u=!0);else{if(">"===o[e+1]){"/"===t?s.selfClosed=!0:""!==i&&s.attributes.push([i+t]),e++;break}i+=t}else u&&(c?"{"===c?(r+=t,"{"===t?n++:"}"===t&&(0<n?n--:e=a(e,[i,r.slice(0,-1)],"props"))):"\\"!==o[e-1]&&t===c?e=a(e,[i,r]):r+=t:/["'`{]/.test(t)?c=t:/[a-zA-Z]/.test(t)&&(""!==i&&s.attributes.push([i]),l=!0,u=!1,i=t));">"===(t=o[++e])&&u&&(r+=t,t=o[++e])}return++e},t.exports})(),e["/lib/build-content/jsx/element.js"]=(()=>{var t={exports:{}};let s=n("/lib/build-content/jsx/attributes/get-attributes.js"),o=n("/lib/build-content/jsx/breckets.js"),e=n("/lib/build-content/jsx/outer.js");class i{tagName="";rest="";inner="";attributes=[];props=[];selfClosed=!1;constructor(t,e,n){if(">"===t[e+1])this.isComponent=!1,this.tagName="",this.i=e+2;else{for(this.isComponent=/[A-Z]/.test(t[e+1]);e<t.length&&!1!==/[A-Za-z0-9.]/.test(t[++e]);)this.tagName+=t[e];n&&this.attributes.push(["component","${this.name}"]),this.i=s(t[e],e,this,t)}!1===this.selfClosed&&this.getInner(t)}get outer(){return e(this)}getInner(t){var e=`</${this.tagName}>`;let n=0;for(var s="</>"==e?"<>":"<"+this.tagName;this.i<t.length;){if(this.inner+=t[this.i],this.inner.endsWith(s)&&n++,this.inner.endsWith(e)){if(!(0<n)){this.inner=this.inner.slice(0,-e.length).trim();break}n--}this.i++}this.buildInner()}buildInner(){let e="";if(!(this.inner.trim().length<2)&&(this.inner.includes("<")||this.inner.includes("{"))){for(let t=0;t<this.inner.length;t++){var n,s;"<"===this.inner[t]?(n=new i(this.inner,t),e+=n.outer,t=n.i):"{"===this.inner[t]?(n=t,t=o(t,this.inner),s=this.inner.slice(n,t),e+="$"+i.jsxParser(s)):e+=this.inner[t]}this.inner=e}}}return t.exports=i,t.exports})(),e["/lib/build-content/jsx/jsx-parser.js"]=(()=>{var t={exports:{}};let r=n("/lib/build-content/jsx/element.js");function e(e,n){let s="";for(let t=0;t<e.length;t++)if("("===e[t]){var o=t;for(t++;0===e[t].trim().length;)t++;if("<"===e[t]){var i=new r(e,t,n);for(t=i.i,s+="`"+i.outer+"`";")"!==e[t];)t++}else s+=e.slice(o,t+1)}else s+=e[t];return s}return r.jsxParser=e,t.exports=e,t.exports})(),e["/lib/build-content/index.js"]=(()=>{var t={exports:{}};let s=n("/lib/build-content/jsx/jsx-parser.js"),o=n("/lib/build-content/build-component/index.js"),i=n("/lib/build-content/remove-comments.js");return t.exports=function(t,e){var n=e.split("/").pop().replace(/\.js$/,""),e=`context.currentPath = '${e}'; | ||
`+s(i(t),n);return o(e,n)},t.exports})(),e["/lib/build.js"]=(()=>{var t={exports:{}};let c=n("/lib/build-content/index.js"),a=n("/lib/context/context.js");return t.exports=function(t,e="context",n,s){var o,i=new a(n,s);for(o in t.contents)t.contents[o]=c(t.contents[o],o);let r="";var{links:l,styles:u}=i;return{resultFn:t.build({},i,e),context:i,add:r=s&&!n||!s&&n?[...l.map(t=>`<link rel="stylesheet" href="${t}">`),u.length?`<style>${u.join("\n")}</style>`:""].filter(Boolean).join("\n")+"\n":r}},t.exports})(),e["/lib/browser.js"]=(()=>{var t={exports:{}};let i=n("/lib/build.js");return t.exports=async function(t,e="body",n={},s){await(t=new Require(t)).getContent();var{resultFn:t,context:s,add:o}=i(t,s,!0,!1);return s.data=n,e&&(e=document.querySelector(e))&&(e.innerHTML=o+t(n).trim(),s.runActions()),s},t.exports})(),e["/lib/browser.js"]}{var e;let s=new Function("return "+"{}")();(function t(e){for(var n in e)"function"==typeof e[n]&&e[n].name===Obj.recursiveName?e[n]=e[n](s):null!==e[n]&&"object"==typeof e[n]&&t(e[n])})(s),s}try{t({})}catch(n){{var s=n;var r={"/lib/context/component.js":{from:4,to:78},"/lib/context/context.js":{from:79,to:137},"/lib/build-content/remove-comments.js":{from:138,to:149},"/lib/build-content/build-component/get-function.js":{from:150,to:181},"/lib/build-content/build-component/index.js":{from:182,to:203},"/lib/build-content/jsx/outer.js":{from:204,to:233},"/lib/build-content/jsx/breckets.js":{from:234,to:258},"/lib/build-content/jsx/attributes/build-action.js":{from:259,to:271},"/lib/build-content/jsx/attributes/build-prop.js":{from:272,to:293},"/lib/build-content/jsx/attributes/get-attributes.js":{from:294,to:365},"/lib/build-content/jsx/element.js":{from:366,to:439},"/lib/build-content/jsx/jsx-parser.js":{from:440,to:467},"/lib/build-content/index.js":{from:468,to:484},"/lib/build.js":{from:485,to:511},"/lib/browser.js":{from:512,to:535}};var l=535;let[t,...e]=s.stack.split("\n");throw e=e.map(t=>{var e=t.match(/<anonymous>:(\d*):(\d*)\)$/);if(e){let n=Number(e[1]);if(n+1!==l){var s,o,e=Number(e[2]),i=Object.entries(r).filter(([,{from:t,to:e}])=>n>=t&&n<=e);if(0!==i.length)return[i,{from:s,to:o}]=i[0],` at ${t.match(/at\s(.*?)\s/)[1]} ${i} (${n-s-2}:${e})`}}}).filter(Boolean),s.stack=t+"\n"+e.join("\n"),s;return}}})(); |
const { describe, it } = require('node:test'); | ||
const assert = require('node:assert'); | ||
const buildAction = require('../lib/build-content/jsx/attributes/build-action'); | ||
const Component = require('../lib/context/component') | ||
describe('buildAction', () => { | ||
@@ -13,10 +13,11 @@ it('should build action correctly and update element attributes', () => { | ||
assert.deepStrictEqual(element.attributes, [['click', '${context.counter++}${context.actions.push({name:this.name,selector:`[click="${context.counter-1}"]`,event:\'click\',fn:handleClick}) ? \'\' : \'\'}']]); | ||
const fn = new Function('context', 'handleClick', 'return `' + element.attributes[0][1] + '`'); | ||
assert.deepStrictEqual(element.attributes, [["click","${this.addAction('click',handleClick)}"]]); | ||
let fn = new Function('context', 'handleClick', 'return `' + element.attributes[0][1] + '`'); | ||
const component = new Component('test') | ||
fn = fn.bind(component) | ||
const handleClick = () => {}; | ||
const result = fn(context, handleClick); | ||
assert.strictEqual(result, '1'); | ||
const expected = { name:'',selector: '[click="1"]', event: 'click', fn: handleClick }; | ||
assert.deepStrictEqual(context.actions[0], expected); | ||
assert.strictEqual(result, 'test0'); | ||
const expected = [ {"event": "click","id": "test0",fn:handleClick}] | ||
assert.deepStrictEqual(component.actions, expected); | ||
}); | ||
@@ -30,12 +31,13 @@ | ||
buildAction(toAdd, element); | ||
assert.deepStrictEqual(element.attributes, [["mouseover","${this.addAction('mouseover',handleMouseOver)}"]]); | ||
assert.deepStrictEqual(element.attributes, [['mouseover', '${context.counter++}${context.actions.push({name:this.name,selector:`[mouseover="${context.counter-1}"]`,event:\'mouseover\',fn:handleMouseOver}) ? \'\' : \'\'}']]); | ||
const fn = new Function('context', 'handleMouseOver', 'return `' + element.attributes[0][1] + '`'); | ||
let fn = new Function('context', 'handleMouseOver', 'return `' + element.attributes[0][1] + '`'); | ||
const component = new Component('test') | ||
fn = fn.bind(component) | ||
const handleMouseOver = () => {}; | ||
const result = fn(context, handleMouseOver); | ||
assert.strictEqual(result, '5'); | ||
const expected = { name:'', selector: '[mouseover="5"]', event: 'mouseover', fn: handleMouseOver }; | ||
assert.deepStrictEqual(context.actions[0], expected); | ||
assert.strictEqual(result, 'test0'); | ||
const expected = { event: 'mouseover', id: 'test0', fn: handleMouseOver }; | ||
assert.deepStrictEqual(component.actions[0], expected); | ||
}); | ||
@@ -47,3 +49,2 @@ | ||
const element = { attributes: [] }; | ||
const context = { counter: 1, actions: [] }; | ||
@@ -53,45 +54,24 @@ buildAction(toAdd1, element); | ||
assert.deepStrictEqual(element.attributes, [ | ||
['click', '${context.counter++}${context.actions.push({name:this.name,selector:`[click="${context.counter-1}"]`,event:\'click\',fn:handleClick}) ? \'\' : \'\'}'], | ||
['mouseover', '${context.counter++}${context.actions.push({name:this.name,selector:`[mouseover="${context.counter-1}"]`,event:\'mouseover\',fn:handleMouseOver}) ? \'\' : \'\'}'] | ||
]); | ||
const expectedAttributes = [ | ||
["click","${this.addAction('click',handleClick)}"], | ||
["mouseover","${this.addAction('mouseover',handleMouseOver)}"] | ||
] | ||
assert.deepStrictEqual(element.attributes, expectedAttributes); | ||
const fnClick = new Function('context', 'handleClick', 'return `' + element.attributes[0][1] + '`'); | ||
const fnMouseOver = new Function('context', 'handleMouseOver', 'return `' + element.attributes[1][1] + '`'); | ||
let fn = new Function('handleClick','handleMouseOver','return `' + element.attributes[0][1] + element.attributes[1][1] + '`') | ||
const component = new Component('test') | ||
fn = fn.bind(component) | ||
const handleClick = () => {}; | ||
const handleMouseOver = () => {}; | ||
const resultClick = fnClick(context, handleClick); | ||
const resultMouseOver = fnMouseOver(context, handleMouseOver); | ||
assert.strictEqual(resultClick, '1'); | ||
assert.strictEqual(resultMouseOver, '2'); | ||
const result = fn(handleClick, handleMouseOver); | ||
assert(result === 'test0test1') | ||
const expectedActions = [ | ||
{ name:'',selector: '[click="1"]', event: 'click', fn: handleClick }, | ||
{ name:'',selector: '[mouseover="2"]', event: 'mouseover', fn: handleMouseOver } | ||
{"event": "click","id": "test0",fn:handleClick}, | ||
{"event": "mouseover","id": "test1",fn:handleMouseOver} | ||
]; | ||
assert.deepStrictEqual(context.actions, expectedActions); | ||
assert.strictEqual(context.counter, 3); | ||
assert.deepStrictEqual(component.actions,expectedActions) | ||
}); | ||
it('should handle empty attributes array in element', () => { | ||
const toAdd = ['onClick', 'handleClick']; | ||
const element = { attributes: [] }; | ||
const context = { counter: 1, actions: [] }; | ||
buildAction(toAdd, element); | ||
assert.deepStrictEqual(element.attributes, [['click', '${context.counter++}${context.actions.push({name:this.name,selector:`[click="${context.counter-1}"]`,event:\'click\',fn:handleClick}) ? \'\' : \'\'}']]); | ||
const fn = new Function('context', 'handleClick', 'return `' + element.attributes[0][1] + '`'); | ||
const handleClick = () => {}; | ||
const result = fn(context, handleClick); | ||
assert.strictEqual(result, '1'); | ||
const expected = { name:'',selector: '[click="1"]', event: 'click', fn: handleClick }; | ||
assert.deepStrictEqual(context.actions[0], expected); | ||
}); | ||
}); |
@@ -46,3 +46,4 @@ const { describe, it } = require('node:test'); | ||
const element = new Element(content, 0); | ||
assert.strictEqual(element.outer, '${MyComponent({},[["prop","value"]],`Inner content`)}'); | ||
const expected = '${MyComponent({prop:"value"},`Inner content`)}' | ||
assert.strictEqual(element.outer, expected); | ||
}); | ||
@@ -49,0 +50,0 @@ |
const { describe, it } = require('node:test'); | ||
const assert = require('node:assert'); | ||
const getAttributes = require('../lib/build-content/jsx/attributes/get-attributes'); | ||
const Component = require('../lib/context/component') | ||
@@ -40,9 +41,14 @@ class Element { | ||
getAttributes(content[4], 4, element, content); | ||
const fn = new Function('context', 'handleClick', 'return `' + element.attributes[0][1] + '`'); | ||
const component = new Component('test') | ||
let fn = new Function('context', 'handleClick', 'return `' + element.attributes[0][1] + '`'); | ||
fn = fn.bind(component) | ||
const handleClick = () => {}; | ||
const result = fn(context, handleClick); | ||
assert(result === '1') | ||
assert(result === 'test0') | ||
assert(element.attributes[0][0] === 'click') | ||
const expected = { name:'',selector: '[click="1"]', event: 'click', fn: handleClick }; | ||
assert.deepStrictEqual(context.actions[0], expected); | ||
const expected = { "event": "click","id": "test0",fn: handleClick}; | ||
assert.deepStrictEqual(component.actions[0], expected); | ||
}); | ||
@@ -49,0 +55,0 @@ |
@@ -29,4 +29,5 @@ const { describe, it } = require('node:test'); | ||
const result = jsxParser(content); | ||
assert.strictEqual(result, 'const element = `${MyComponent({},[["prop","value"]],`Content`)}`;'); | ||
const expected = 'const element = `${MyComponent({prop:"value"},`Content`)}`;' | ||
assert.strictEqual(result, expected); | ||
}); | ||
}); |
@@ -47,3 +47,4 @@ const { describe, it } = require('node:test'); | ||
const result = getOuter(element); | ||
assert.strictEqual(result, '${MyComponent({prop1:value1,prop2:value2,...restProps},[],`Child content`)}'); | ||
const expected = '${MyComponent({prop1:value1,prop2:value2,...restProps},`Child content`)}' | ||
assert.strictEqual(result, expected); | ||
}); | ||
@@ -58,3 +59,3 @@ | ||
const result = getOuter(element); | ||
assert.strictEqual(result, '${SimpleComponent({},[],`Inner text`)}'); | ||
assert.strictEqual(result, '${SimpleComponent({},`Inner text`)}'); | ||
}); | ||
@@ -61,0 +62,0 @@ |
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
124638
57
2038
149
10
4