als-layout 2.5.2 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/#.md +0 -8
- package/docs/0-change-log.md +3 -20
- package/docs/1-basic-usage.md +1 -1
- package/docs/4-render.md +31 -58
- package/lib/layout.js +9 -25
- package/lib/render.js +12 -0
- package/package.json +2 -1
- package/readme.md +36 -88
- package/tests/counter/App.js +6 -0
- package/tests/counter/build.js +14 -0
- package/tests/counter/counter.js +14 -0
- package/tests/elements.test.js +4 -3
- package/tests/layout.test.js +0 -101
- package/tests/render/App.js +10 -0
- package/tests/render/Some.js +8 -0
- package/tests/render.test.js +31 -0
- package/lib/render/build-app.js +0 -23
- package/lib/render/component-hierarchy.js +0 -21
- package/lib/render/update.js +0 -19
- package/tests/build-app.test.js +0 -80
- package/tests/component-hierarchy.test.js +0 -51
- package/tests/run-tests.js +0 -6
package/docs/#.md
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
# als-layout Documentation
|
|
2
2
|
|
|
3
|
-
## Library Description
|
|
4
|
-
|
|
5
|
-
### What is it?
|
|
6
3
|
`als-layout` is a JavaScript library designed to simplify and enhance the process of constructing and managing web page layouts. It provides a comprehensive API for modifying HTML documents dynamically, allowing developers to add, update, and manipulate various elements such as meta tags, styles, scripts, and more.
|
|
7
4
|
|
|
8
|
-
### Why is it needed?
|
|
9
|
-
Managing HTML document structures and their contents can often become repetitive and error-prone when done manually. `als-modular` offers a structured and reusable approach, reducing development time and improving code maintainability.
|
|
10
|
-
|
|
11
|
-
### What can you do with it and where can it be used?
|
|
12
5
|
The `als-layout` library is versatile, suitable for:
|
|
13
6
|
- Building dynamic web pages that require frequent updates to their metadata, styles, or scripts.
|
|
14
7
|
- Creating templating systems where multiple page layouts share similar structures but differ in content or styling.
|
|
15
8
|
- Developing web applications that need to dynamically adjust their UI based on user interactions or data changes.
|
|
16
|
-
This library is particularly useful in environments where rapid development and modular design are prioritized.
|
|
17
9
|
|
|
18
10
|
## Installation and Adding
|
|
19
11
|
To use the `als-layout` library in your project, you can install it via npm and then include it in your JavaScript files:
|
package/docs/0-change-log.md
CHANGED
|
@@ -1,23 +1,6 @@
|
|
|
1
1
|
## Change Log
|
|
2
|
-
* V2.3.0
|
|
3
|
-
* Default, component will not be included in $App, if not [publish] attribute
|
|
4
|
-
* When cloning, components and other staff cloned too
|
|
5
|
-
* Reference does not change
|
|
6
2
|
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Now each Layout extends Document (als-document)
|
|
11
|
-
* layout.$ and layout.$$
|
|
12
|
-
* Also $App.$ and $App.$$ on backend too
|
|
13
|
-
* no langs validation any more
|
|
14
|
-
* lang method instead
|
|
15
|
-
* no layout.cached
|
|
16
|
-
* charset meta tag allready exists by default
|
|
17
|
-
|
|
18
|
-
* V2.1.0
|
|
19
|
-
* updated als-document version
|
|
20
|
-
* [part] attribute for static components
|
|
21
|
-
* onload() method
|
|
22
|
-
* bugs fixed
|
|
3
|
+
* V3.0.0
|
|
4
|
+
* render switched to als-render
|
|
5
|
+
* updated bug with meta tags after body
|
|
23
6
|
|
package/docs/1-basic-usage.md
CHANGED
|
@@ -44,6 +44,6 @@ By adding onload attribute, you can run scripts for each element after dom conte
|
|
|
44
44
|
|
|
45
45
|
Example how it works:
|
|
46
46
|
```js
|
|
47
|
-
const layout = new Layout().
|
|
47
|
+
const layout = new Layout().viewport().title('On load').onload()
|
|
48
48
|
layout.body.innerHTML = /*html*/`<div onload="this.innerHTML = 'new content'">original content</div>`
|
|
49
49
|
```
|
package/docs/4-render.md
CHANGED
|
@@ -1,71 +1,44 @@
|
|
|
1
1
|
## Rendering
|
|
2
|
+
Since version 3.0 , `als-layout` using `als-render` for rendering.
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
### Structure
|
|
6
|
-
The layout object houses several key properties that facilitate dynamic content management:
|
|
7
|
-
|
|
8
|
-
- `layout.data`: Stores the data that can be used across the page, such as state variables or configuration settings.
|
|
9
|
-
- `layout.components`: Holds functions that define the behavior and rendering logic for components identified by specific attributes in the HTML.
|
|
10
|
-
- `layout.utils`: Contains utility functions that can be used throughout the page for common tasks.
|
|
11
|
-
- `layout.actions`: Methods that can be triggered by user interaction, often modifying `layout.data` and updating the page accordingly.
|
|
4
|
+
### Counter Example
|
|
5
|
+
To demonstrate dynamic interaction, consider a counter that can be increased or decreased through user input:
|
|
12
6
|
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
The App.js
|
|
8
|
+
```jsx
|
|
9
|
+
const Counter = require('./counter')
|
|
10
|
+
function App() {
|
|
11
|
+
context.count = 0
|
|
12
|
+
return (<Counter />)
|
|
13
|
+
}
|
|
14
|
+
module.exports = App
|
|
15
|
+
```
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
$(selector, parent = document), // Equivalent to querySelector
|
|
23
|
-
$$(selector, parent = document), // Equivalent to querySelectorAll
|
|
24
|
-
update(element) { // Updates the element if it has a component attribute
|
|
25
|
-
// Update logic here
|
|
17
|
+
counter.js
|
|
18
|
+
```jsx
|
|
19
|
+
function Counter() {
|
|
20
|
+
function change(m=1) {
|
|
21
|
+
context.count += 1*m
|
|
22
|
+
Counter.update()
|
|
26
23
|
}
|
|
24
|
+
|
|
25
|
+
return (<div>
|
|
26
|
+
<button onclick={() => change(1)}>Increase</button>
|
|
27
|
+
<span component="counter">{context.count}</span>
|
|
28
|
+
<button onclick={() => change(-1)}>Decrease</button>
|
|
29
|
+
</div>)
|
|
27
30
|
}
|
|
28
|
-
```
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
module.exports = Counter
|
|
33
|
+
```
|
|
32
34
|
|
|
35
|
+
build.js
|
|
33
36
|
```js
|
|
34
37
|
const fs = require('fs')
|
|
35
38
|
const Layout = require('als-layout')
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// Define a component for displaying the counter
|
|
42
|
-
layout.components.counter = function(element, $App) {
|
|
43
|
-
element.innerHTML = `${$App.data.counter}`
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Define actions for increasing and decreasing the counter
|
|
47
|
-
layout.actions = {
|
|
48
|
-
increase: () => { $App.data.counter++; $App.update($App.$('[component=counter]')); },
|
|
49
|
-
decrease: () => { $We render the html page to measure and write to a document.app.data.counter--; $Page updates are shown in real-time on the rendered HTML.$App.update($App.$('[component=counter]')); }
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Add buttons and the counter display to the body
|
|
53
|
-
layout.body.innerHTML = /*html*/`
|
|
54
|
-
<button onclick="$App.actions.increase()">Increase</button>
|
|
55
|
-
<span component="counter" publish></span>
|
|
56
|
-
<button onclick="$App.actions.decrease()">Decrease</button>
|
|
57
|
-
`
|
|
58
|
-
|
|
59
|
-
// Measure render time and generate HTML
|
|
60
|
-
const time1 = performance.now()
|
|
61
|
-
const rawHtml = layout.render()
|
|
62
|
-
const time2 = performance.now()
|
|
63
|
-
console.log(`${time2 - time1}ms`) // e.g., 1.0649ms
|
|
64
|
-
|
|
65
|
-
// Write the output to a file
|
|
66
|
-
fs.writeFileSync('counter.html', rawHtml, 'utf-8')
|
|
39
|
+
const layout = new Layout()
|
|
40
|
+
.title('Counter')
|
|
41
|
+
.render('./App', 'body', {minified:true,update:true})
|
|
42
|
+
fs.writeFileSync('counter.html', layout.rawHtml, 'utf-8') // Write the output to a file
|
|
67
43
|
```
|
|
68
44
|
|
|
69
|
-
### Advanced Rendering Details
|
|
70
|
-
- **Component Indexing:** Each component is assigned a `componentIndex` during rendering, providing a unique index within its parent component.
|
|
71
|
-
- **Publish Attribute:** Using the `publish` attribute in a component make it being added to `$App.components`, unless it is nested within another component.
|
package/lib/layout.js
CHANGED
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
const { Document } = require('als-document')
|
|
2
|
-
const build$App = require('./render/build-app')
|
|
3
2
|
const onload = require('./onload')
|
|
4
|
-
const
|
|
3
|
+
const render = require('./render')
|
|
5
4
|
const {
|
|
6
|
-
addKeywords, addStyle, addDescription, addTitle, addImage,
|
|
7
|
-
addUrl, addFavicon, addScript,addLink,charset,viewport
|
|
5
|
+
addKeywords, addStyle, addDescription, addTitle, addImage,
|
|
6
|
+
addUrl, addFavicon, addScript, addLink, charset, viewport
|
|
8
7
|
} = require('./elements/index')
|
|
9
8
|
|
|
10
9
|
class Layout extends Document {
|
|
11
|
-
constructor(html,url,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
this.components = components
|
|
15
|
-
this.data = data
|
|
16
|
-
this.actions = actions
|
|
17
|
-
this.utils = utils
|
|
18
|
-
this.root = this.html
|
|
19
|
-
this.update = update
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
onload() {this.script({},onload); return this}
|
|
23
|
-
lang(lang) {this.html.setAttribute('lang',lang); return this}
|
|
10
|
+
constructor(html, url) {super(html, url); this.root = this.html}
|
|
11
|
+
onload() { this.script({}, onload); return this }
|
|
12
|
+
lang(lang) { this.html.setAttribute('lang', lang); return this }
|
|
24
13
|
version(v) { this.v = v; return this; }
|
|
25
14
|
charset(newCharset = 'UTF-8') { return charset(newCharset, this) }
|
|
26
15
|
keywords(keywords = []) { return addKeywords(keywords, this) }
|
|
@@ -33,14 +22,9 @@ class Layout extends Document {
|
|
|
33
22
|
script(attributes, innerHTML, head = true, v = this.v) { return addScript(attributes, innerHTML, head, v, this) }
|
|
34
23
|
link(href, v = this.v) { return addLink(href, v, this) }
|
|
35
24
|
viewport(newViewport = 'width=device-width, initial-scale=1.0') { return viewport(newViewport, this) }
|
|
36
|
-
get rawHtml() {return this.innerHTML}
|
|
37
|
-
get clone() {return new Layout(new Document(this),this.URL
|
|
38
|
-
render() {
|
|
39
|
-
const done = []
|
|
40
|
-
this.update(this.root, done)
|
|
41
|
-
this.script({}, build$App(done,this),false)
|
|
42
|
-
return this.rawHtml
|
|
43
|
-
}
|
|
25
|
+
get rawHtml() { return this.innerHTML }
|
|
26
|
+
get clone() { return new Layout(new Document(this), this.URL) }
|
|
27
|
+
render(path, selector, makeBundle) {return render(path, selector, makeBundle,this)}
|
|
44
28
|
}
|
|
45
29
|
|
|
46
30
|
module.exports = Layout
|
package/lib/render.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const render = require('als-render')
|
|
2
|
+
module.exports = function (path, selector, makeBundle, layout) {
|
|
3
|
+
const element = layout.$(selector)
|
|
4
|
+
if (!element) return layout
|
|
5
|
+
const { rawHtml, bundle } = render(path)
|
|
6
|
+
element.innerHTML = rawHtml
|
|
7
|
+
if (typeof makeBundle === 'object') {
|
|
8
|
+
const { update = true, minified = true } = makeBundle
|
|
9
|
+
layout.script({}, bundle(selector, update, minified), false)
|
|
10
|
+
}
|
|
11
|
+
return layout
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "als-layout",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "Html layout constructor",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"als-css-parser": "^0.5.0",
|
|
18
18
|
"als-document": "^1.4.0",
|
|
19
|
+
"als-render": "^0.2.4",
|
|
19
20
|
"als-simple-css": "^9.1.0"
|
|
20
21
|
}
|
|
21
22
|
}
|
package/readme.md
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
# als-layout Documentation
|
|
2
2
|
|
|
3
|
-
## Library Description
|
|
4
|
-
|
|
5
|
-
### What is it?
|
|
6
3
|
`als-layout` is a JavaScript library designed to simplify and enhance the process of constructing and managing web page layouts. It provides a comprehensive API for modifying HTML documents dynamically, allowing developers to add, update, and manipulate various elements such as meta tags, styles, scripts, and more.
|
|
7
4
|
|
|
8
|
-
### Why is it needed?
|
|
9
|
-
Managing HTML document structures and their contents can often become repetitive and error-prone when done manually. `als-modular` offers a structured and reusable approach, reducing development time and improving code maintainability.
|
|
10
|
-
|
|
11
|
-
### What can you do with it and where can it be used?
|
|
12
5
|
The `als-layout` library is versatile, suitable for:
|
|
13
6
|
- Building dynamic web pages that require frequent updates to their metadata, styles, or scripts.
|
|
14
7
|
- Creating templating systems where multiple page layouts share similar structures but differ in content or styling.
|
|
15
8
|
- Developing web applications that need to dynamically adjust their UI based on user interactions or data changes.
|
|
16
|
-
This library is particularly useful in environments where rapid development and modular design are prioritized.
|
|
17
9
|
|
|
18
10
|
## Installation and Adding
|
|
19
11
|
To use the `als-layout` library in your project, you can install it via npm and then include it in your JavaScript files:
|
|
@@ -27,27 +19,10 @@ const Layout = require('als-layout')
|
|
|
27
19
|
```
|
|
28
20
|
|
|
29
21
|
## Change Log
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
*
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
* V2.2.0
|
|
36
|
-
* fixed empty update function if no components for $App
|
|
37
|
-
* if component function return string, it will element.innerHTML = string
|
|
38
|
-
* Now each Layout extends Document (als-document)
|
|
39
|
-
* layout.$ and layout.$$
|
|
40
|
-
* Also $App.$ and $App.$$ on backend too
|
|
41
|
-
* no langs validation any more
|
|
42
|
-
* lang method instead
|
|
43
|
-
* no layout.cached
|
|
44
|
-
* charset meta tag allready exists by default
|
|
45
|
-
|
|
46
|
-
* V2.1.0
|
|
47
|
-
* updated als-document version
|
|
48
|
-
* [part] attribute for static components
|
|
49
|
-
* onload() method
|
|
50
|
-
* bugs fixed
|
|
22
|
+
|
|
23
|
+
* V3.0.0
|
|
24
|
+
* render switched to als-render
|
|
25
|
+
* updated bug with meta tags after body
|
|
51
26
|
|
|
52
27
|
|
|
53
28
|
## Basic Usage
|
|
@@ -96,7 +71,7 @@ By adding onload attribute, you can run scripts for each element after dom conte
|
|
|
96
71
|
|
|
97
72
|
Example how it works:
|
|
98
73
|
```js
|
|
99
|
-
const layout = new Layout().
|
|
74
|
+
const layout = new Layout().viewport().title('On load').onload()
|
|
100
75
|
layout.body.innerHTML = /*html*/`<div onload="this.innerHTML = 'new content'">original content</div>`
|
|
101
76
|
```
|
|
102
77
|
## Cloning Functionality
|
|
@@ -164,73 +139,46 @@ This advanced example illustrates how `als-layout` can be used to handle complex
|
|
|
164
139
|
|
|
165
140
|
|
|
166
141
|
## Rendering
|
|
142
|
+
Since version 3.0 , `als-layout` using `als-render` for rendering.
|
|
167
143
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
### Structure
|
|
171
|
-
The layout object houses several key properties that facilitate dynamic content management:
|
|
172
|
-
|
|
173
|
-
- `layout.data`: Stores the data that can be used across the page, such as state variables or configuration settings.
|
|
174
|
-
- `layout.components`: Holds functions that define the behavior and rendering logic for components identified by specific attributes in the HTML.
|
|
175
|
-
- `layout.utils`: Contains utility functions that can be used throughout the page for common tasks.
|
|
176
|
-
- `layout.actions`: Methods that can be triggered by user interaction, often modifying `layout.data` and updating the page accordingly.
|
|
144
|
+
### Counter Example
|
|
145
|
+
To demonstrate dynamic interaction, consider a counter that can be increased or decreased through user input:
|
|
177
146
|
|
|
178
|
-
|
|
179
|
-
|
|
147
|
+
The App.js
|
|
148
|
+
```jsx
|
|
149
|
+
const Counter = require('./counter')
|
|
150
|
+
function App() {
|
|
151
|
+
context.count = 0
|
|
152
|
+
return (<Counter />)
|
|
153
|
+
}
|
|
154
|
+
module.exports = App
|
|
155
|
+
```
|
|
180
156
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
$(selector, parent = document), // Equivalent to querySelector
|
|
188
|
-
$$(selector, parent = document), // Equivalent to querySelectorAll
|
|
189
|
-
update(element) { // Updates the element if it has a component attribute
|
|
190
|
-
// Update logic here
|
|
157
|
+
counter.js
|
|
158
|
+
```jsx
|
|
159
|
+
function Counter() {
|
|
160
|
+
function change(m=1) {
|
|
161
|
+
context.count += 1*m
|
|
162
|
+
Counter.update()
|
|
191
163
|
}
|
|
164
|
+
|
|
165
|
+
return (<div>
|
|
166
|
+
<button onclick={() => change(1)}>Increase</button>
|
|
167
|
+
<span component="counter">{context.count}</span>
|
|
168
|
+
<button onclick={() => change(-1)}>Decrease</button>
|
|
169
|
+
</div>)
|
|
192
170
|
}
|
|
193
|
-
```
|
|
194
171
|
|
|
195
|
-
|
|
196
|
-
|
|
172
|
+
module.exports = Counter
|
|
173
|
+
```
|
|
197
174
|
|
|
175
|
+
build.js
|
|
198
176
|
```js
|
|
199
177
|
const fs = require('fs')
|
|
200
178
|
const Layout = require('als-layout')
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
// Define a component for displaying the counter
|
|
207
|
-
layout.components.counter = function(element, $App) {
|
|
208
|
-
element.innerHTML = `${$App.data.counter}`
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Define actions for increasing and decreasing the counter
|
|
212
|
-
layout.actions = {
|
|
213
|
-
increase: () => { $App.data.counter++; $App.update($App.$('[component=counter]')); },
|
|
214
|
-
decrease: () => { $We render the html page to measure and write to a document.app.data.counter--; $Page updates are shown in real-time on the rendered HTML.$App.update($App.$('[component=counter]')); }
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Add buttons and the counter display to the body
|
|
218
|
-
layout.body.innerHTML = /*html*/`
|
|
219
|
-
<button onclick="$App.actions.increase()">Increase</button>
|
|
220
|
-
<span component="counter" publish></span>
|
|
221
|
-
<button onclick="$App.actions.decrease()">Decrease</button>
|
|
222
|
-
`
|
|
223
|
-
|
|
224
|
-
// Measure render time and generate HTML
|
|
225
|
-
const time1 = performance.now()
|
|
226
|
-
const rawHtml = layout.render()
|
|
227
|
-
const time2 = performance.now()
|
|
228
|
-
console.log(`${time2 - time1}ms`) // e.g., 1.0649ms
|
|
229
|
-
|
|
230
|
-
// Write the output to a file
|
|
231
|
-
fs.writeFileSync('counter.html', rawHtml, 'utf-8')
|
|
179
|
+
const layout = new Layout()
|
|
180
|
+
.title('Counter')
|
|
181
|
+
.render('./App', 'body', {minified:true,update:true})
|
|
182
|
+
fs.writeFileSync('counter.html', layout.rawHtml, 'utf-8') // Write the output to a file
|
|
232
183
|
```
|
|
233
184
|
|
|
234
|
-
### Advanced Rendering Details
|
|
235
|
-
- **Component Indexing:** Each component is assigned a `componentIndex` during rendering, providing a unique index within its parent component.
|
|
236
|
-
- **Publish Attribute:** Using the `publish` attribute in a component make it being added to `$App.components`, unless it is nested within another component.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const Layout = require('../../lib/layout')
|
|
3
|
+
|
|
4
|
+
// Create and configure the layout
|
|
5
|
+
const layout = new Layout().title('Counter')
|
|
6
|
+
|
|
7
|
+
// Measure render time and generate HTML
|
|
8
|
+
const time1 = performance.now()
|
|
9
|
+
const { rawHtml } = layout.render('./App', 'body', {minified:true})
|
|
10
|
+
const time2 = performance.now()
|
|
11
|
+
console.log(`${time2 - time1}ms`) // e.g., 1.0649ms
|
|
12
|
+
|
|
13
|
+
// Write the output to a file
|
|
14
|
+
fs.writeFileSync('counter.html', rawHtml, 'utf-8')
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function Counter() {
|
|
2
|
+
function change(m=1) {
|
|
3
|
+
context.count += 1*m
|
|
4
|
+
Counter.update()
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
return (<div>
|
|
8
|
+
<button onclick={() => change(1)}>Increase</button>
|
|
9
|
+
<span component="counter">{context.count}</span>
|
|
10
|
+
<button onclick={() => change(-1)}>Decrease</button>
|
|
11
|
+
</div>)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = Counter
|
package/tests/elements.test.js
CHANGED
|
@@ -47,11 +47,12 @@ describe('Favicon tests', () => {
|
|
|
47
47
|
assert.strictEqual(layout.root.$('link[rel="icon"]').getAttribute('href'), newHref, 'Favicon href should be updated');
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
it('should insert favicon at
|
|
50
|
+
it('should insert favicon at the end of head if not present', () => {
|
|
51
51
|
const faviconHref = 'favicon.ico';
|
|
52
52
|
layout.favicon(faviconHref);
|
|
53
|
-
|
|
54
|
-
assert.strictEqual(
|
|
53
|
+
const element = layout.head.childNodes[layout.head.childNodes.length - 1]
|
|
54
|
+
assert.strictEqual(element.tagName, 'LINK', 'Favicon link should be at second position');
|
|
55
|
+
assert.strictEqual(element.getAttribute('href'), faviconHref, 'Favicon href not set correctly');
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
it('should add favicon correctly', () => {
|
package/tests/layout.test.js
CHANGED
|
@@ -74,107 +74,6 @@ describe('HTML Structure Initialization', () => {
|
|
|
74
74
|
});
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
describe('Component Updating', () => {
|
|
78
|
-
let layout;
|
|
79
|
-
|
|
80
|
-
beforeEach(() => {
|
|
81
|
-
layout = new Layout();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should correctly update components', () => {
|
|
85
|
-
layout.components.test2 = (element, $App) => {
|
|
86
|
-
assert(element.componentIndex === 0)
|
|
87
|
-
element.innerHTML = 'test2 success'
|
|
88
|
-
}
|
|
89
|
-
layout.components.test1 = (element, $App) => {
|
|
90
|
-
assert(element.componentIndex === 0)
|
|
91
|
-
element.insert(2, 'test1 success')
|
|
92
|
-
}
|
|
93
|
-
layout.body.innerHTML = /*html*/`<div component="test1">
|
|
94
|
-
<div component="test2">failed</div>
|
|
95
|
-
</div>`
|
|
96
|
-
|
|
97
|
-
const done = []
|
|
98
|
-
layout.update(layout.root, done)
|
|
99
|
-
assert(done.length === 2)
|
|
100
|
-
assert(layout.rawHtml.includes('test1 success'))
|
|
101
|
-
assert(layout.rawHtml.includes('test2 success'))
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('should correctly render components', () => {
|
|
105
|
-
layout.components.test2 = (element, $App) => {
|
|
106
|
-
assert(element.componentIndex === 0)
|
|
107
|
-
element.innerHTML = 'test2 success'
|
|
108
|
-
}
|
|
109
|
-
layout.components.test1 = (element, $App) => {
|
|
110
|
-
assert(element.componentIndex === 0)
|
|
111
|
-
element.insert(2, 'test1 success')
|
|
112
|
-
}
|
|
113
|
-
layout.data.test = 'hello'
|
|
114
|
-
layout.body.innerHTML = /*html*/`<div component="test1" publish>
|
|
115
|
-
<div component="test2">failed</div>
|
|
116
|
-
</div>`
|
|
117
|
-
|
|
118
|
-
const rawHtml = layout.render()
|
|
119
|
-
assert(rawHtml.includes('test1 success'))
|
|
120
|
-
assert(rawHtml.includes('test2 success'))
|
|
121
|
-
|
|
122
|
-
const script = rawHtml.match(/<[^>]*?script.*?>[^<]*<\/[^>]*?script.*?>/g)[0]
|
|
123
|
-
.replace('<script>window.$App = ', '')
|
|
124
|
-
.replace('</script>', '')
|
|
125
|
-
const fn = new Function(`return ${script}`)
|
|
126
|
-
const { browser, data, components, actions, utils, updata, $, $$ } = fn()
|
|
127
|
-
assert(browser === true)
|
|
128
|
-
assert.deepStrictEqual(Object.keys(components), ['test1', 'test2'])
|
|
129
|
-
assert(components.test1.toString() === layout.components.test1.toString())
|
|
130
|
-
assert(data.test === 'hello')
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it('should add part to $App if it is component`s descendant', () => {
|
|
134
|
-
layout.body.innerHTML = /*html*/`
|
|
135
|
-
<div component="parent" publish>
|
|
136
|
-
<div component="child"></div>
|
|
137
|
-
</div>`;
|
|
138
|
-
layout.components.parent = (element, $App) => {};
|
|
139
|
-
layout.components.child = (element, $App) => {};
|
|
140
|
-
|
|
141
|
-
const rawHtml = layout.render();
|
|
142
|
-
const script = rawHtml.match(/<[^>]*?script.*?>[^<]*<\/[^>]*?script.*?>/g)[0]
|
|
143
|
-
.replace('<script>window.$App = ', '')
|
|
144
|
-
.replace('</script>', '')
|
|
145
|
-
const fn = new Function(`return ${script}`)
|
|
146
|
-
|
|
147
|
-
const {components} = fn()
|
|
148
|
-
assert(components.parent !== undefined)
|
|
149
|
-
assert(components.child !== undefined)
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('should not add part to $App if it has no component ancestor', () => {
|
|
153
|
-
layout.body.innerHTML = /*html*/`
|
|
154
|
-
<div component="comp1" publish></div>
|
|
155
|
-
<div component="comp2"></div>
|
|
156
|
-
`;
|
|
157
|
-
layout.components.comp1 = (element, $App) => {};
|
|
158
|
-
layout.components.comp2 = (element, $App) => {};
|
|
159
|
-
|
|
160
|
-
const rawHtml = layout.render();
|
|
161
|
-
const script = rawHtml.match(/<[^>]*?script.*?>[^<]*<\/[^>]*?script.*?>/g)[0]
|
|
162
|
-
.replace('<script>window.$App = ', '')
|
|
163
|
-
.replace('</script>', '')
|
|
164
|
-
const fn = new Function(`return ${script}`)
|
|
165
|
-
|
|
166
|
-
const {components} = fn()
|
|
167
|
-
assert(components.comp1 !== undefined)
|
|
168
|
-
assert(components.comp2 === undefined)
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('If no components, should add empty update function to $App', () => {
|
|
172
|
-
const rawHtml = layout.render();
|
|
173
|
-
assert( rawHtml.includes('update:()=>{}'));
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
});
|
|
177
|
-
|
|
178
77
|
describe('Clone Testing', () => {
|
|
179
78
|
let layout;
|
|
180
79
|
beforeEach(() => {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const { describe, it,beforeEach } = require('node:test')
|
|
3
|
+
const Layout = require('../lib/layout')
|
|
4
|
+
|
|
5
|
+
describe('Basic tests', () => {
|
|
6
|
+
let layout
|
|
7
|
+
beforeEach(() => layout = new Layout())
|
|
8
|
+
it('Should render without bundle',() => {
|
|
9
|
+
const { rawHtml } = layout.render('./render/App', 'body')
|
|
10
|
+
const expected =
|
|
11
|
+
'<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title></title></head><body><div>\n' +
|
|
12
|
+
' <div>Hello from App!</div>\n' +
|
|
13
|
+
' <div component="Some">\n' +
|
|
14
|
+
' Hello From Some!\n' +
|
|
15
|
+
' </div>\n' +
|
|
16
|
+
' </div></body></html>'
|
|
17
|
+
|
|
18
|
+
assert(rawHtml === expected)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('Should render with bundle',() => {
|
|
22
|
+
const { rawHtml } = layout.render('./render/App', 'body',{})
|
|
23
|
+
assert(rawHtml.includes('<script>'))
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('Should do nothing if element not exists',() => {
|
|
27
|
+
const { rawHtml } = layout.render('./render/App', 'main')
|
|
28
|
+
const expected = '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title></title></head><body></body></html>'
|
|
29
|
+
assert(rawHtml === expected)
|
|
30
|
+
})
|
|
31
|
+
})
|
package/lib/render/build-app.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
const componentHierarchy = require('./component-hierarchy')
|
|
2
|
-
module.exports = function(done=[],layout) {
|
|
3
|
-
let strComponents = 'components:{}', updateFn = 'update:()=>{}'
|
|
4
|
-
const components = componentHierarchy(done)
|
|
5
|
-
if(components.length) {
|
|
6
|
-
strComponents = 'components:{'+components.map(componentName => {
|
|
7
|
-
return `"${componentName}":${layout.components[componentName].toString()}`
|
|
8
|
-
}).join(',')+'}'
|
|
9
|
-
updateFn = `update:${layout.update.toString()}`
|
|
10
|
-
}
|
|
11
|
-
const strData = 'data:'+JSON.stringify(layout.data)
|
|
12
|
-
const $ = 'function $(selector,parent = document) {return parent.querySelector(selector)}'
|
|
13
|
-
const $$ = 'function $$(selector,parent = document) {return [...parent.querySelectorAll(selector)]}'
|
|
14
|
-
let actions = 'actions:{'
|
|
15
|
-
for(const actionName in layout.actions) {
|
|
16
|
-
actions += `${actionName}:${layout.actions[actionName].toString()},`
|
|
17
|
-
}
|
|
18
|
-
let utils = 'utils:{'
|
|
19
|
-
for(const utilsName in layout.utils) {
|
|
20
|
-
utils += `${utilsName}:${layout.utils[utilsName].toString()},`
|
|
21
|
-
}
|
|
22
|
-
return `window.$App = {browser:true,${strData},${strComponents},${actions}},${utils}},${updateFn},$:${$},$$:${$$}}`
|
|
23
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
function componentHierarchy(elements) {
|
|
2
|
-
const entries = elements
|
|
3
|
-
.map(el => ([el, el.getAttribute('component'), el.ancestors]))
|
|
4
|
-
.sort((a, b) => a[2].length - b[2].length)
|
|
5
|
-
|
|
6
|
-
const result = []
|
|
7
|
-
while(entries.length > 0) {
|
|
8
|
-
const [element, componentName, ancestors] = entries.shift()
|
|
9
|
-
const partsInside = element.$$('[component]')
|
|
10
|
-
if(element.getAttribute('publish') === null) {
|
|
11
|
-
element.removeAttribute('component')
|
|
12
|
-
element.removeAttribute('publish')
|
|
13
|
-
continue
|
|
14
|
-
}
|
|
15
|
-
partsInside.forEach(element => { element.setAttribute('publish','') });
|
|
16
|
-
if(!result.includes(componentName)) result.push(componentName)
|
|
17
|
-
}
|
|
18
|
-
return result
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
module.exports = componentHierarchy
|
package/lib/render/update.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
function update(element, done = []) {
|
|
2
|
-
for (const componentName in this.components) {
|
|
3
|
-
const fn = this.components[componentName]
|
|
4
|
-
let components = element.getAttribute('component') === componentName
|
|
5
|
-
? [element]
|
|
6
|
-
: [...element.querySelectorAll(`[component=${componentName}]`)]
|
|
7
|
-
components = components.filter(el => !done.includes(el))
|
|
8
|
-
components.forEach((element, i) => {
|
|
9
|
-
element.componentIndex = i
|
|
10
|
-
const result = fn(element, this)
|
|
11
|
-
if(typeof result === 'string') element.innerHTML = result
|
|
12
|
-
done.push(element)
|
|
13
|
-
this.update(element, done)
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
module.exports = update
|
package/tests/build-app.test.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
const assert = require('assert');
|
|
2
|
-
const { describe, it } = require('node:test');
|
|
3
|
-
const build$App = require('../lib/render/build-app');
|
|
4
|
-
|
|
5
|
-
describe('build-$App Functionality', () => {
|
|
6
|
-
|
|
7
|
-
it('should handle empty input arrays correctly', () => {
|
|
8
|
-
const done = [];
|
|
9
|
-
const layout = {
|
|
10
|
-
components: {},
|
|
11
|
-
};
|
|
12
|
-
const result = build$App(done, layout);
|
|
13
|
-
assert.strictEqual(result, 'window.$App = {browser:true,data:undefined,components:{},actions:{},utils:{},update:()=>{},$:function $(selector,parent = document) {return parent.querySelector(selector)},$$:function $$(selector,parent = document) {return [...parent.querySelectorAll(selector)]}}');
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('should process multiple components correctly', () => {
|
|
17
|
-
const called = []
|
|
18
|
-
const done = [
|
|
19
|
-
{
|
|
20
|
-
getAttribute: (attr) => attr === 'component' ? 'comp1' : attr === 'publish' ? '' : null,
|
|
21
|
-
removeAttribute(att) {called.push(att)},
|
|
22
|
-
$$(){return []},
|
|
23
|
-
ancestors:2
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
getAttribute: (attr) => attr === 'component' ? 'comp2' : attr === 'publish' ? '' : null,
|
|
27
|
-
removeAttribute(att) {called.push(att)},
|
|
28
|
-
$$(){return []},
|
|
29
|
-
ancestors:2
|
|
30
|
-
}
|
|
31
|
-
];
|
|
32
|
-
const layout = {
|
|
33
|
-
components: {
|
|
34
|
-
comp1: () => 'Component1',
|
|
35
|
-
comp2: () => 'Component2'
|
|
36
|
-
},
|
|
37
|
-
actions: {},
|
|
38
|
-
utils: {},
|
|
39
|
-
data: {},
|
|
40
|
-
update: function update() { }
|
|
41
|
-
};
|
|
42
|
-
const result = build$App(done, layout);
|
|
43
|
-
assert(result.includes(`"comp1":${layout.components.comp1.toString()}`))
|
|
44
|
-
assert(result.includes(`"comp2":${layout.components.comp2.toString()}`))
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should add action functions correctly', () => {
|
|
48
|
-
const done = [];
|
|
49
|
-
const layout = {
|
|
50
|
-
components: {},
|
|
51
|
-
actions: {
|
|
52
|
-
action1: () => console.log('Action 1'),
|
|
53
|
-
action2: () => console.log('Action 2')
|
|
54
|
-
},
|
|
55
|
-
utils: {},
|
|
56
|
-
data: {},
|
|
57
|
-
update: function update() { }
|
|
58
|
-
};
|
|
59
|
-
const result = build$App(done, layout);
|
|
60
|
-
assert(result.includes(`action1:${layout.actions.action1.toString()}`))
|
|
61
|
-
assert(result.includes(`action2:${layout.actions.action2.toString()}`))
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should integrate utilities correctly', () => {
|
|
65
|
-
const done = [];
|
|
66
|
-
const layout = {
|
|
67
|
-
components: {},
|
|
68
|
-
actions: {},
|
|
69
|
-
utils: {
|
|
70
|
-
util1: () => console.log('Utility 1'),
|
|
71
|
-
util2: () => console.log('Utility 2')
|
|
72
|
-
},
|
|
73
|
-
data: {},
|
|
74
|
-
update: function update() { }
|
|
75
|
-
};
|
|
76
|
-
const result = build$App(done, layout);
|
|
77
|
-
assert(result.includes(`util1:${layout.utils.util1.toString()}`))
|
|
78
|
-
assert(result.includes(`util2:${layout.utils.util2.toString()}`))
|
|
79
|
-
});
|
|
80
|
-
});
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
const { parseHTML } = require('als-document')
|
|
2
|
-
const assert = require('assert');
|
|
3
|
-
const { describe, it } = require('node:test')
|
|
4
|
-
const componentHierarchy = require('../lib/render/component-hierarchy')
|
|
5
|
-
|
|
6
|
-
function shuffleArray(array) {
|
|
7
|
-
for (let i = array.length - 1; i > 0; i--) {
|
|
8
|
-
const j = Math.floor(Math.random() * (i + 1)); // Получаем случайный индекс от 0 до i
|
|
9
|
-
[array[i], array[j]] = [array[j], array[i]]; // Обмен элементов местами
|
|
10
|
-
}
|
|
11
|
-
return array;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
describe('Basic tests', () => {
|
|
15
|
-
it('should build correct hierarchy', () => {
|
|
16
|
-
const root = parseHTML(/*html*/`<div component="some" publish>
|
|
17
|
-
<div>
|
|
18
|
-
<div component="some1"></div>
|
|
19
|
-
<div>
|
|
20
|
-
<div component="some2">
|
|
21
|
-
<div component="some3"></div>
|
|
22
|
-
</div>
|
|
23
|
-
<div component="some2"></div>
|
|
24
|
-
</div>
|
|
25
|
-
|
|
26
|
-
</div>
|
|
27
|
-
</div>`)
|
|
28
|
-
|
|
29
|
-
const result = componentHierarchy(shuffleArray(root.$$('[component]')))
|
|
30
|
-
assert.deepStrictEqual(result,[ 'some', 'some1', 'some2', 'some3' ])
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('should build correct hierarchy', () => {
|
|
34
|
-
const root = parseHTML(/*html*/`<div component="some">
|
|
35
|
-
<div>
|
|
36
|
-
<div component="some1"></div>
|
|
37
|
-
<div>
|
|
38
|
-
<div component="some2">
|
|
39
|
-
<div component="some3"></div>
|
|
40
|
-
</div>
|
|
41
|
-
<div component="some2">
|
|
42
|
-
<div component="some"></div>
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
|
|
46
|
-
</div>
|
|
47
|
-
</div>`)
|
|
48
|
-
|
|
49
|
-
assert.doesNotThrow(() => componentHierarchy(shuffleArray(root.$$('[component]'))))
|
|
50
|
-
})
|
|
51
|
-
})
|