als-layout 2.5.1 → 3.0.0

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 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:
@@ -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
- * V2.2.0
8
- * fixed empty update function if no components for $App
9
- * if component function return string, it will element.innerHTML = string
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
 
@@ -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().charset().viewport().title('On load').onload()
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
- Each instance of `Layout` comes equipped with a `render` method that compiles the HTML structure and embeds a JavaScript object to manage the page dynamically. This object, known as `window.$App`, allows for real-time interaction and updates within the page.
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
- ### The Render Method
14
- The `render` method processes all elements with a `component` attribute, dynamically generating their content and behavior based on the defined components. After rendering, the page includes the `window.$App` JavaScript object, which provides methods and properties to interact with the page elements and data:
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
- ```js
17
- window.$App = {
18
- data, // Access to the layout data object
19
- components, // Access to components functions
20
- utils, // Access to utility functions
21
- actions, // Access to action functions
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
- ### Counter Example
31
- To demonstrate dynamic interaction, consider a counter that can be increased or decreased through user input:
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
- // Create and configure the layout
38
- const layout = new Layout().title('Counter')
39
- layout.data.counter = 0 // Initialize counter data
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 update = require('./render/update')
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,doc = {}) {
12
- super(html,url)
13
- const {components = {},data = {},actions = {},utils = {}} = doc
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,this)}
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": "2.5.1",
3
+ "version": "3.0.0",
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.31",
19
20
  "als-simple-css": "^9.1.0"
20
21
  }
21
22
  }
package/readme.md ADDED
@@ -0,0 +1,184 @@
1
+ # als-layout Documentation
2
+
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.
4
+
5
+ The `als-layout` library is versatile, suitable for:
6
+ - Building dynamic web pages that require frequent updates to their metadata, styles, or scripts.
7
+ - Creating templating systems where multiple page layouts share similar structures but differ in content or styling.
8
+ - Developing web applications that need to dynamically adjust their UI based on user interactions or data changes.
9
+
10
+ ## Installation and Adding
11
+ To use the `als-layout` library in your project, you can install it via npm and then include it in your JavaScript files:
12
+
13
+ ```bash
14
+ npm i als-layout
15
+ ```
16
+
17
+ ```js
18
+ const Layout = require('als-layout')
19
+ ```
20
+
21
+ ## Change Log
22
+
23
+ * V3.0.0
24
+ * render switched to als-render
25
+ * updated bug with meta tags after body
26
+
27
+
28
+ ## Basic Usage
29
+
30
+ ### Initialization
31
+ To start using `als-layout`, you first need to create a new instance of `Layout`. This instance will serve as the foundation for building and modifying your web page.
32
+
33
+ ```js
34
+ const Layout = require('als-layout');
35
+ const layout = new Layout();
36
+ ```
37
+
38
+ ### Adding Different Elements
39
+ Once you have your `Layout` instance, you can easily add or modify various elements of your web page. Here are some examples of how you can use the library to customize your layout:
40
+
41
+ ```js
42
+ const layout = new Layout()
43
+ .charset() // default UTF-8
44
+ .viewport() // default width=device-width, initial-scale=1.0
45
+ .title('Test title') // adding/updating title and meta[og:title]
46
+ .favicon('/favicon.png') // adding/updating link[rel=icon][type=image/x-icon] with new href
47
+ .keywords(['some', 'keyword']) // adding/updating meta[name=keywords]. not adding existing keywords
48
+ .image('/main-image.png', '1.5') // adding/updating meta - og:image, twitter:image, twitter:card
49
+ .description('Cool site') // adding/updating meta og:description, twitter:description, and description tag
50
+ .version('1.0.0') // adds version parameter to link, script.src, and image
51
+ .url('/some', 'http://site.com') // adding/updating meta[og:url] and link[rel="canonical"]
52
+ .style([{body:{m:0, bgc:'whitesmoke'}}]) // adding as simple-css styles to existing/new style tag
53
+ .style('body {margin:0; background-color:whitesmoke;}', true) // adding css styles to existing/new style tag. Second parameter is minified (default=false).
54
+ .link('/styles.css', '2.0') // adding link[rel=stylesheet] if such href not exists
55
+ .script({src:'/app.js'}, '', true, '3.0') // set script with src to head if such src not exists
56
+ .script({}, 'console.log("hello world")', false) // set script with script code to footer
57
+
58
+ // Accessors for document parts
59
+ layout.body // getter for body element (if not exists, created)
60
+ layout.head // getter for head element (if not exists, created)
61
+ layout.html // getter for html Element (if not exists, created)
62
+
63
+ // Outputs
64
+ layout.rawHtml // raw HTML of the document
65
+ layout.clone // creates a new layout object clone for current object
66
+ ```
67
+
68
+ ### onload
69
+
70
+ By adding onload attribute, you can run scripts for each element after dom content has loaded.
71
+
72
+ Example how it works:
73
+ ```js
74
+ const layout = new Layout().viewport().title('On load').onload()
75
+ layout.body.innerHTML = /*html*/`<div onload="this.innerHTML = 'new content'">original content</div>`
76
+ ```
77
+ ## Cloning Functionality
78
+
79
+ ### What is Cloning and Why is it Necessary?
80
+ Cloning in the `als-layout` library refers to creating a complete, independent copy of the existing `Layout` instance. This functionality is crucial when you need to generate multiple pages or versions of a page from a single base layout without affecting the original setup.
81
+
82
+ ### How to Use Cloning
83
+ To clone a `Layout` instance, simply use the `clone` method. This method creates a new `Layout` instance with the same properties and settings as the original, allowing for independent modifications without interference.
84
+
85
+ ```js
86
+ const newLayout = layout.clone;
87
+ ```
88
+
89
+ ### Benefits of Cloning
90
+ - **Efficiency:** Cloning is highly efficient, especially for creating pages with similar structures but different content or styles. It avoids the overhead of reinitializing and reconfiguring a new `Layout` instance from scratch.
91
+ - **Speed:** Cloning is fast, typically taking less than 20ms even for large pages. This makes it ideal for high-performance web applications that need to dynamically generate content.
92
+ - **Isolation:** Changes made to a cloned `Layout` do not affect the original, ensuring that each instance can be modified independently based on specific requirements.
93
+
94
+ Cloning is particularly useful in scenarios where templates or base layouts are used repeatedly with slight variations, providing a robust and scalable solution for web page generation.
95
+
96
+
97
+ ## Advanced Usage
98
+
99
+ The `als-layout` library allows for sophisticated manipulation of web page layouts, providing robust tools for creating dynamic and complex web pages. Below is an advanced example demonstrating various capabilities of the library:
100
+
101
+ ```js
102
+ const Layout = require('als-layout')
103
+
104
+ // Starting with a basic HTML template and specifying the host for URL methods
105
+ const raw = /*html*/`<html></html>`
106
+ const host = 'http://example.com';
107
+ const layout = new Layout(raw, host).lang('fr')
108
+ console.log(layout.rawHtml)
109
+ // <!DOCTYPE html><html lang="fr"><head></p></head><body></body></html>
110
+
111
+ // Cloning the initial layout to create a specialized page
112
+ const homePage = layout.clone
113
+ homeAutoReload = layout.clone
114
+ homePage.title('Home page')
115
+ homePage.body.innerHTML = /*html*/`<h1>Home page</h1>`
116
+ console.log(homePage.rawHtml)
117
+ // <!DOCTYPE html><html lang="fr"><head><title>Home page</title><meta property="og:title" content="Home page"></head><body><h1>Home page</h1></body></html>
118
+
119
+ // Adding script that reloads the page every minute
120
+ homeAutoReload.script({}, 'setTimeout(function() { window.location.reload(); }, 60000);', false)
121
+ console.log(homeAutoReload.rawHtml)
122
+ // <!DOCTYPE html><html lang="fr"><head><title>Automatic Reload Page</title><meta property="og:title" content="Automatic Reload Page"></head><body><script>setTimeout(function() { window.location.reload(); }, 60000);</script></body></html>
123
+
124
+ // Demonstrating dynamic stylesheet linkage with versioning
125
+ const styleVersion = '1.1';
126
+ homePage.link('/css/main.css', styleVersion)
127
+ console.log(homePage.rawHtml)
128
+ // Includes link to the stylesheet with version parameter to ensure fresh cache
129
+ ```
130
+
131
+ In this example:
132
+ - We start with a basic HTML template and use the `lang` method to set the language.
133
+ - We use the `clone` method to create two versions of the base layout: one for the home page and another that automatically reloads every minute.
134
+ - We manipulate the `body` of the `homePage` to include custom HTML.
135
+ - We add a script to `homeAutoReload` that sets up an automatic page reload, showcasing how to insert JavaScript dynamically.
136
+ - We dynamically add a versioned link to a stylesheet in the `homePage`, demonstrating control over caching and resource management.
137
+
138
+ This advanced example illustrates how `als-layout` can be used to handle complex scenarios and requirements in web development, enhancing the flexibility and power at your disposal.
139
+
140
+
141
+ ## Rendering
142
+ Since version 3.0 , `als-layout` using `als-render` for rendering.
143
+
144
+ ### Counter Example
145
+ To demonstrate dynamic interaction, consider a counter that can be increased or decreased through user input:
146
+
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
+ ```
156
+
157
+ counter.js
158
+ ```jsx
159
+ function Counter() {
160
+ function change(m=1) {
161
+ context.count += 1*m
162
+ Counter.update()
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>)
170
+ }
171
+
172
+ module.exports = Counter
173
+ ```
174
+
175
+ build.js
176
+ ```js
177
+ const fs = require('fs')
178
+ const Layout = require('als-layout')
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
183
+ ```
184
+
@@ -0,0 +1,6 @@
1
+ const Counter = require('./counter')
2
+ function App() {
3
+ context.count = 0
4
+ return (<Counter />)
5
+ }
6
+ module.exports = App
@@ -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
@@ -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 second position if not present', () => {
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
- assert.strictEqual(layout.head.childNodes[0].tagName, 'LINK', 'Favicon link should be at second position');
54
- assert.strictEqual(layout.head.childNodes[0].getAttribute('href'), faviconHref, 'Favicon href not set correctly');
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', () => {
@@ -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,10 @@
1
+ const Some = require('./Some')
2
+
3
+ function App() {
4
+ return (<div>
5
+ <div>Hello from App!</div>
6
+ <Some/>
7
+ </div>)
8
+ }
9
+
10
+ module.exports = App
@@ -0,0 +1,8 @@
1
+ function Some() {
2
+ return (<div>
3
+ Hello From Some!
4
+
5
+ </div>)
6
+ }
7
+
8
+ module.exports = Some
@@ -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
+ })
@@ -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
@@ -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
@@ -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
- })
@@ -1,6 +0,0 @@
1
- const { execSync } = require('child_process');
2
- let [,, path] = process.argv;
3
-
4
- execSync(`node --test ./tests/${path}.test.js`, { stdio: 'inherit' });
5
-
6
- // Example: npm test mw.cookie