als-layout 4.3.0 → 5.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/index.js CHANGED
@@ -1,182 +1,3 @@
1
- const { Document, SingleNode, Node } = require('als-document');
2
- const UglifyJS = require("uglify-js");
3
- const uglifycss = require('uglifycss');
4
- const { join, dirname } = require('path')
5
- const { writeFileSync, mkdirSync } = require('fs')
6
- const Render = require('als-render')
7
-
8
- const onloadScript = /*js*/`document.addEventListener('DOMContentLoaded', function() {
9
- const elements = document.querySelectorAll('[onload]');
10
- elements.forEach(element => {
11
- const onloadCode = element.getAttribute('onload');
12
- const func = Function('"use strict"; return function() { ' + onloadCode + ' }');
13
- func().call(element);
14
- element.removeAttribute('onload');
15
- });
16
- });`;
17
-
18
- class Layout extends Document {
19
- static bundles = {};
20
- static viewsPath = '../../views';
21
- static publicPath = '../../public';
22
- static dev = true;
23
- constructor(html, host, minified = false) {
24
- super(html, host);
25
- this.minified = minified
26
- this.root = this.html
27
- }
28
-
29
- get rawHtml() { return this.innerHTML }
30
- get clone() { return new Layout(new Document(this), this.URL, this.minified) }
31
- lang(lang) { this.html.setAttribute('lang', lang); return this }
32
-
33
- onload() {
34
- if (this.onloadAdded) return
35
- this.script({}, onloadScript);
36
- this.onloadAdded = true
37
- return this
38
- }
39
-
40
- title(title) {
41
- super.title // create title tag if not exists
42
- super.title = title
43
- this.meta({ property: 'og:title', content: title })
44
- return this
45
- }
46
-
47
- description(description) {
48
- this.meta({ name: 'description', content: description })
49
- this.meta({ property: 'og:description', content: description })
50
- this.meta({ property: 'twitter:description', content: description })
51
- return this
52
- }
53
-
54
- favicon(href) {
55
- const faviconElement = this.root.$('link[rel=icon][type=image/x-icon]')
56
- if (faviconElement) faviconElement.setAttribute('href', href)
57
- else this.head.insert(2, new SingleNode('link', { rel: 'icon', href, type: 'image/x-icon' }))
58
- return this
59
- }
60
-
61
- meta(props) {
62
- const entries = Object.entries(props)
63
- const [name, value] = entries[0]
64
- const selector = `meta[${name}="${value}"]`
65
- const metaElement = this.root.$(selector)
66
- if (metaElement) entries.forEach(([name, v]) => metaElement.setAttribute(name, props[name]))
67
- else this.head.insert(2, new SingleNode('meta', props))
68
- }
69
-
70
- keywords(keywords = []) {
71
- let keywordsElement = this.root.$('meta[name=keywords]')
72
- if (!keywordsElement) keywordsElement = new SingleNode('meta', { name: 'keywords' })
73
- const content = keywordsElement.getAttribute('content')
74
- const existingKeywords = content ? content.split(',') : []
75
- keywords.forEach(keyword => {
76
- keyword = keyword.trim()
77
- if (!existingKeywords.includes(keyword)) existingKeywords.push(keyword)
78
- });
79
- if (existingKeywords.length) {
80
- keywordsElement.setAttribute('content', existingKeywords.join())
81
- this.head.insert(2, keywordsElement)
82
- }
83
- return this
84
- }
85
-
86
- viewport(viewport = 'width=device-width, initial-scale=1.0') {
87
- const element = this.root.$('meta[name="viewport"]')
88
- if (element) element.setAttribute('content', viewport)
89
- else this.head.insert(2, new SingleNode('meta', { name: 'viewport', content: viewport }))
90
- return this
91
- }
92
-
93
- image(image) {
94
- this.meta({ property: 'og:image', content: image })
95
- this.meta({ name: 'twitter:image', content: image })
96
- this.meta({ name: 'twitter:card', content: 'summary_large_image' })
97
- return this
98
- }
99
-
100
- style(styles, minified = this.minified) {
101
- if (typeof styles !== 'string') throw 'styles parameter should be string';
102
- if (minified) styles = uglifycss.processString(styles);
103
- let styleElement = this.root.$('style')
104
- if (styleElement) styleElement.innerHTML = styleElement.innerHTML + '\n' + styles
105
- else {
106
- styleElement = new Node('style')
107
- styleElement.innerHTML = styles
108
- this.head.insert(2, styleElement)
109
- }
110
- return this
111
- }
112
-
113
- url(url, host = this.URL) {
114
- try {
115
- url = new URL(url, host).href.replace(/\/$/, '')
116
- this.meta({ property: 'og:url', content: url })
117
- const canonicalElement = this.root.$('link[rel="canonical"]')
118
- if (canonicalElement) canonicalElement.setAttribute('href', url)
119
- else this.head.insert(2, new SingleNode('link', { rel: 'canonical', href: url }))
120
- } catch (error) {
121
- console.log(`url ${url} with host ${host} is not valid url`)
122
- }
123
- return this
124
- }
125
-
126
- script(attrs = {}, innerHTML = '', head = true, minified = this.minified) {
127
- if (typeof attrs !== 'object' || attrs === null || Array.isArray(attrs)) attrs = {}
128
- if (attrs.src) {
129
- const selector = `script[src="${attrs.src}"]`
130
- if (attrs.src && this.root.$(selector)) return this
131
- }
132
- if (Object.keys(attrs).length || innerHTML) {
133
- const script = new Node('script', attrs)
134
- if (innerHTML) {
135
- if (minified) innerHTML = UglifyJS.minify(innerHTML).code
136
- script.innerHTML = innerHTML
137
- }
138
- if (head) this.head.insert(2, script)
139
- else this.html.insert(2, script)
140
- }
141
- return this
142
- }
143
-
144
- link(href, attributes = { rel: "stylesheet", type: "text/css" }) {
145
- if (!href || typeof href !== 'string') return this
146
- const selector = `link[rel=stylesheet][href^="${href}"]`
147
- let linkElement = this.root.$(selector)
148
- if (linkElement) return
149
- linkElement = new SingleNode('link', { href, ...attributes })
150
- this.head.insert(2, linkElement)
151
- return this
152
- }
153
-
154
- view(path, data = {}, includeBundle=true) {
155
- const parts = path.split('/')
156
- if(parts[parts.length-1][0].toUpperCase() !== parts[parts.length-1][0]) parts.push('Index.js')
157
- const relativePath = join(Layout.viewsPath, ...parts)
158
- const { bundle, bundleFn, callBundle, rawHtml, context } = Render.render(relativePath, data, !Layout.dev)
159
- if (includeBundle) {
160
- if (Layout.dev) this.script({}, bundle, false)
161
- else {
162
- this.write('context', context, true)
163
- this.write(path, bundleFn, false)
164
- this.script({}, callBundle, false)
165
- }
166
- }
167
- const element = /*html*/`<content></content>`
168
- this.body.insertAdjacentHTML('afterbegin', element)
169
- return this.rawHtml.replace(element, rawHtml)
170
- }
171
-
172
- write(path, content, head) {
173
- if (Layout.bundles[path]) return
174
- const filePath = join(Layout.publicPath, 'js', path + '.js')
175
- mkdirSync(dirname(filePath), { recursive: true })
176
- writeFileSync(filePath, content)
177
- Layout.bundles[path] = path
178
- this.script({ src: `/js/${path}.js` }, '', head)
179
- }
180
- }
1
+ const Layout = require('./lib/layout')
181
2
 
182
3
  module.exports = Layout
package/lib/layout.js ADDED
@@ -0,0 +1,129 @@
1
+ const { Document, SingleNode } = require('als-document');
2
+ const script = require('./script');
3
+ const style = require('./style');
4
+ const onloadScript = require('./onload');
5
+
6
+ class Layout extends Document {
7
+ constructor(html, options = {}) {
8
+ super(html, options.host);
9
+ this.options = options
10
+ this.logger = options.logger || console
11
+ this.root = this.html
12
+ this.statusCode = 200
13
+ }
14
+
15
+ get rawHtml() { return this.innerHTML }
16
+ get clone() { return new this.constructor(new Document(this, this.URL), this.options) }
17
+ lang(lang) { this.html.setAttribute('lang', lang); return this }
18
+
19
+ onload() {
20
+ const id = 'onload-script'
21
+ if(this.$('#'+id)) return
22
+ this.script({id}, onloadScript);
23
+ return this
24
+ }
25
+
26
+ title(title) {
27
+ super.title // create title tag if not exists
28
+ super.title = title
29
+ this.meta({ property: 'og:title', content: title })
30
+ return this
31
+ }
32
+
33
+ description(description) {
34
+ this.meta({ name: 'description', content: description })
35
+ this.meta({ property: 'og:description', content: description })
36
+ this.meta({ property: 'twitter:description', content: description })
37
+ return this
38
+ }
39
+
40
+ favicon(href) {
41
+ const faviconElement = this.root.$('link[rel=icon][type=image/x-icon]')
42
+ if (faviconElement) faviconElement.setAttribute('href', href)
43
+ else this.head.insert(2, new SingleNode('link', { rel: 'icon', href, type: 'image/x-icon' }))
44
+ return this
45
+ }
46
+
47
+ meta(props) {
48
+ const entries = Object.entries(props)
49
+ const [name, value] = entries[0]
50
+ const selector = `meta[${name}="${value}"]`
51
+ const metaElement = this.root.$(selector)
52
+ if (metaElement) entries.forEach(([name, v]) => metaElement.setAttribute(name, props[name]))
53
+ else this.head.insert(2, new SingleNode('meta', props))
54
+ }
55
+
56
+ keywords(keywords = []) {
57
+ let keywordsElement = this.root.$('meta[name=keywords]')
58
+ if (!keywordsElement) keywordsElement = new SingleNode('meta', { name: 'keywords' })
59
+ const content = keywordsElement.getAttribute('content')
60
+ const existingKeywords = content ? content.split(',') : []
61
+ keywords.forEach(keyword => {
62
+ keyword = keyword.trim()
63
+ if (!existingKeywords.includes(keyword)) existingKeywords.push(keyword)
64
+ });
65
+ if (existingKeywords.length) {
66
+ keywordsElement.setAttribute('content', existingKeywords.join())
67
+ this.head.insert(2, keywordsElement)
68
+ }
69
+ return this
70
+ }
71
+
72
+ viewport(viewport = 'width=device-width, initial-scale=1.0') {
73
+ const element = this.root.$('meta[name="viewport"]')
74
+ if (element) element.setAttribute('content', viewport)
75
+ else this.head.insert(2, new SingleNode('meta', { name: 'viewport', content: viewport }))
76
+ return this
77
+ }
78
+
79
+ image(image) {
80
+ this.meta({ property: 'og:image', content: image })
81
+ this.meta({ name: 'twitter:image', content: image })
82
+ this.meta({ name: 'twitter:card', content: 'summary_large_image' })
83
+ return this
84
+ }
85
+
86
+ style(styles, minified = this.options.minified) {
87
+ return style(styles,minified,this)
88
+ }
89
+
90
+ url(url, host = this.URL) {
91
+ try {
92
+ url = new URL(url, host).href.replace(/\/$/, '')
93
+ this.meta({ property: 'og:url', content: url })
94
+ const canonicalElement = this.root.$('link[rel="canonical"]')
95
+ if (canonicalElement) canonicalElement.setAttribute('href', url)
96
+ else this.head.insert(2, new SingleNode('link', { rel: 'canonical', href: url }))
97
+ } catch (error) {
98
+ this.logger.log(`url ${url} with host ${host} is not valid url`)
99
+ }
100
+ return this
101
+ }
102
+
103
+ script(attrs = {}, innerHTML = '', head = true, minified = this.options.minified) {
104
+ return script(attrs,innerHTML,head,minified,this)
105
+ }
106
+
107
+ link(href, attributes = { rel: "stylesheet", type: "text/css" }) {
108
+ if (!href || typeof href !== 'string') return this
109
+ const selector = `link[rel=stylesheet][href^="${href}"]`
110
+ let linkElement = this.root.$(selector)
111
+ if (linkElement) return
112
+ linkElement = new SingleNode('link', { href, ...attributes })
113
+ this.head.insert(2, linkElement)
114
+ return this
115
+ }
116
+
117
+ status(statusCode) { this.statusCode = statusCode; return this }
118
+
119
+ end(res,statusCode = this.statusCode, rawHtml = this.rawHtml) {
120
+ res.writeHead(statusCode);
121
+ if (res.set) res.set('Content-Type', 'text/html') // for Express
122
+ else res.setHeader('Content-Type', 'text/html') // for http
123
+ res.end(rawHtml)
124
+ return this
125
+ }
126
+
127
+ }
128
+
129
+ module.exports = Layout
package/lib/onload.js ADDED
@@ -0,0 +1,11 @@
1
+ const onloadScript = /*js*/`document.addEventListener('DOMContentLoaded', function() {
2
+ const elements = document.querySelectorAll('[onload]');
3
+ elements.forEach(element => {
4
+ const onloadCode = element.getAttribute('onload');
5
+ const func = Function('"use strict"; return function() { ' + onloadCode + ' }');
6
+ func().call(element);
7
+ element.removeAttribute('onload');
8
+ });
9
+ });`;
10
+
11
+ module.exports = onloadScript
package/lib/script.js ADDED
@@ -0,0 +1,25 @@
1
+ const UglifyJS = require("uglify-js");
2
+ const { Node } = require('als-document');
3
+
4
+ function script(attrs = {}, innerHTML = '', head = true, minified, layout) {
5
+ if (typeof attrs !== 'object' || attrs === null || Array.isArray(attrs)) attrs = {}
6
+ if (attrs.src) {
7
+ const selector = `script[src="${attrs.src}"]`
8
+ if (attrs.src && layout.root.$(selector)) return layout
9
+ }
10
+ if (Object.keys(attrs).length || innerHTML) {
11
+ const script = new Node('script', attrs)
12
+ if (innerHTML) {
13
+ if (minified) {
14
+ try { innerHTML = UglifyJS.minify(innerHTML).code }
15
+ catch (error) { layout.logger.error(error) }
16
+ }
17
+ script.innerHTML = innerHTML
18
+ }
19
+ if (head) layout.head.insert(2, script)
20
+ else layout.html.insert(2, script)
21
+ }
22
+ return layout
23
+ }
24
+
25
+ module.exports = script
package/lib/style.js ADDED
@@ -0,0 +1,20 @@
1
+ const uglifycss = require('uglifycss');
2
+ const { Node } = require('als-document');
3
+
4
+ function style(styles, minified, layout) {
5
+ if (typeof styles !== 'string') throw 'styles parameter should be string';
6
+ if (minified) {
7
+ try { styles = uglifycss.processString(styles); }
8
+ catch (error) { layout.logger.error(error) }
9
+ }
10
+ let styleElement = layout.root.$('style')
11
+ if (styleElement) styleElement.innerHTML = styleElement.innerHTML + '\n' + styles
12
+ else {
13
+ styleElement = new Node('style')
14
+ styleElement.innerHTML = styles
15
+ layout.head.insert(2, styleElement)
16
+ }
17
+ return layout
18
+ }
19
+
20
+ module.exports = style
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "als-layout",
3
- "version": "4.3.0",
3
+ "version": "5.0.1",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "node --test --experimental-test-coverage",
@@ -12,11 +12,7 @@
12
12
  "description": "Html layout constructor",
13
13
  "dependencies": {
14
14
  "als-document": "^1.4.0",
15
- "als-render": "^0.9.3",
16
15
  "uglify-js": "^3.19.2",
17
16
  "uglifycss": "^0.0.29"
18
- },
19
- "devDependencies": {
20
- "fs-extra": "^11.2.0"
21
17
  }
22
18
  }
package/readme.md CHANGED
@@ -19,7 +19,14 @@ const Layout = require('als-layout')
19
19
  ```
20
20
 
21
21
  ## Change Log
22
-
22
+ * V5.0.0
23
+ * constructor changed
24
+ * options object parameter instead separated parameters
25
+ * options.logger
26
+ * bugs fixed
27
+ * some refactoring
28
+ * minifiying error catching
29
+ * status and end methods added
23
30
  * V4.2.0
24
31
  * link method changed
25
32
  * `link(href, attributes = { rel: "stylesheet", type: "text/css" })`
@@ -66,11 +73,12 @@ const layout = new Layout()
66
73
  .image('/main-image.png', '1.5') // adding/updating meta - og:image, twitter:image, twitter:card
67
74
  .description('Cool site') // adding/updating meta og:description, twitter:description, and description tag
68
75
  .url('/some', 'http://site.com') // adding/updating meta[og:url] and link[rel="canonical"]
69
- .style([{body:{m:0, bgc:'whitesmoke'}}]) // adding as simple-css styles to existing/new style tag
70
76
  .style('body {margin:0; background-color:whitesmoke;}', true) // adding css styles to existing/new style tag. Second parameter is minified (default=false).
71
77
  .link('/styles.css') // adding link[rel=stylesheet] if such href not exists
72
78
  .script({src:'/app.js'}, '', true) // set script with src to head if such src not exists
73
79
  .script({}, 'console.log("hello world")', false) // set script with script code to footer
80
+ .status(200)
81
+ .end(response);
74
82
 
75
83
  // Accessors for document parts
76
84
  layout.body // getter for body element (if not exists, created)
@@ -121,8 +129,13 @@ const Layout = require('als-layout')
121
129
  // Starting with a basic HTML template and specifying the host for URL methods
122
130
  const raw = /*html*/`<html></html>`
123
131
  const host = 'http://example.com';
124
- const layout = new Layout(raw, host, minified=false).lang('fr')
125
- console.log(layout.rawHtml)
132
+ const options = {
133
+ logger,
134
+ host,
135
+ minified=false
136
+ }
137
+ const layout = new Layout(raw, options).lang('fr')
138
+ console.log(layout.rawHtml)
126
139
  // <!DOCTYPE html><html lang="fr"><head></p></head><body></body></html>
127
140
 
128
141
  // Cloning the initial layout to create a specialized page
@@ -145,6 +158,7 @@ console.log(homePage.rawHtml)
145
158
  ```
146
159
 
147
160
  In this example:
161
+
148
162
  - We start with a basic HTML template and use the `lang` method to set the language.
149
163
  - 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.
150
164
  - We manipulate the `body` of the `homePage` to include custom HTML.
@@ -153,22 +167,99 @@ In this example:
153
167
 
154
168
  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.
155
169
 
156
- ## View method
170
+ ## API
157
171
 
158
- **The view method is a pilot. Don't use in production.**
172
+ ### Constructor
159
173
 
160
- ```js
161
- Layout.viewsPath = '../../views'; // relative to layout path
162
- Layout.publicPath = '../../public'; // public folder path for urls starts with "/" should be relative to root
163
- Layout.dev = false; // true by default
174
+ #### `new Layout(html: string, options: object)`
164
175
 
165
- const data = {};
166
- const includeBundle = false; // true by default
167
- layout.view('some/Test',data,includeBundle) // viewsPath/some/Test.js
168
- layout.view('some/test',data,includeBundle) // viewsPath/some/test/Index.js
176
+ Creates a new `Layout` instance.
169
177
 
170
- ```
178
+ - **html**: The initial HTML document string.
179
+ - **options**: Configuration options for the layout instance.
180
+ - **minified**: Boolean (default: `false`). Determines if inline CSS and JavaScript should be minified.
181
+ - **logger**: Function (default: `console`). Function for errors in minifiying and url method.
182
+ - **host**: String (defult:`undefined`). String for url method.
183
+
184
+
185
+ ### Properties
186
+
187
+ #### `layout.rawHtml`
188
+
189
+ Returns the inner HTML of the document.
190
+
191
+ #### `layout.clone`
192
+
193
+ Creates a clone of the current layout instance, preserving `options`.
194
+
195
+ ### Methods
196
+
197
+ #### `lang(lang: string): this`
198
+
199
+ Sets the `lang` attribute on the `<html>` element.
200
+
201
+ #### `onload(): this`
202
+
203
+ Adds an `onload` script to the document. If called multiple times, the script is only added once.
204
+
205
+ #### `title(title: string): this`
206
+
207
+ Sets the document title and creates an Open Graph title meta tag.
208
+
209
+ #### `description(description: string): this`
210
+
211
+ Adds description meta tags for SEO and social platforms.
212
+
213
+ #### `favicon(href: string): this`
214
+
215
+ Sets or updates the favicon URL.
216
+
217
+ #### `meta(props: object): this`
218
+
219
+ Adds or updates a `<meta>` tag with specified attributes.
220
+
221
+ - **props**: An object where each key-value pair corresponds to a meta attribute.
222
+
223
+ #### `keywords(keywords: array): this`
224
+
225
+ Sets or appends keywords in the `content` attribute of a `<meta name="keywords">` tag.
226
+
227
+ #### `viewport(viewport: string): this`
228
+
229
+ Sets the viewport meta tag. Default: `width=device-width, initial-scale=1.0`.
230
+
231
+ #### `image(image: string): this`
232
+
233
+ Sets meta tags for Open Graph and Twitter cards for an image.
234
+
235
+ #### `style(styles: string, minified: boolean = this.options.minified): this`
236
+
237
+ Adds inline CSS to the document. If `minified` is true, the CSS is minified.
238
+
239
+ - **styles**: CSS string.
240
+ - **minified**: Boolean (optional).
241
+
242
+ #### `url(url: string, host: string = this.URL): this`
243
+
244
+ Sets the canonical URL and Open Graph URL meta tags.
245
+
246
+ #### `script(attrs: object = {}, innerHTML: string = '', head: boolean = true, minified: boolean = this.options.minified): this`
247
+
248
+ Adds a `<script>` tag to the document.
249
+
250
+ - **attrs**: Attributes for the `<script>` tag.
251
+ - **innerHTML**: Inline JavaScript (optional).
252
+ - **head**: Boolean (default: `true`). If `false`, the script is added to `<body>`.
253
+ - **minified**: Boolean. If true, inline JavaScript is minified.
254
+
255
+ #### `link(href: string, attributes: object = { rel: "stylesheet", type: "text/css" }): this`
256
+
257
+ Adds a `<link>` tag for external stylesheets.
258
+
259
+ #### `status(statusCode: number): this`
260
+
261
+ Sets the HTTP status code for the response.
262
+
263
+ #### `end(res: object): this`
171
264
 
172
- * `includeBundle`
173
- * in dev mode, will include bundle for rendering inside script tag in html
174
- * in prod mode, will save context and bundle as files in publicPath/js folder and will add script[src] to html for those files.
265
+ Sends the HTML response with the specified `statusCode` and sets `Content-Type` to `text/html`.
@@ -22,7 +22,7 @@ describe('Layout Initialization', () => {
22
22
 
23
23
  it('should allow setting a custom host', () => {
24
24
  const customHost = 'http://localhost';
25
- const layout = new Layout(undefined,customHost);
25
+ const layout = new Layout(undefined,{host:customHost});
26
26
  assert.strictEqual(layout.URL, customHost, `host is not set to ${customHost}`);
27
27
  });
28
28
  });
package/docs/#.md DELETED
@@ -1,19 +0,0 @@
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
- ```
@@ -1,25 +0,0 @@
1
- ## Change Log
2
-
3
- * V4.2.0
4
- * link method changed
5
- * `link(href, attributes = { rel: "stylesheet", type: "text/css" })`
6
- * no version method
7
- * no version parameter in image, link,script
8
- * script
9
- * script in footer added one after other in right order
10
-
11
- * V4.1.0
12
- * Render removed
13
-
14
- * V4.0.0
15
- * All code rebuilded and refactored
16
- * No als-simple-css for style
17
- * No charset method
18
- * minifying for style and inner scripts
19
- * updated render version
20
- * render as element's method instead layout's method
21
-
22
- * V3.0.0
23
- * render switched to als-render
24
- * updated bug with meta tags after body
25
-
@@ -1,47 +0,0 @@
1
- ## Basic Usage
2
-
3
- ### Initialization
4
- 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.
5
-
6
- ```js
7
- const Layout = require('als-layout');
8
- const layout = new Layout();
9
- ```
10
-
11
- ### Adding Different Elements
12
- 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:
13
-
14
- ```js
15
- const layout = new Layout()
16
- .viewport() // default width=device-width, initial-scale=1.0
17
- .title('Test title') // adding/updating title and meta[og:title]
18
- .favicon('/favicon.png') // adding/updating link[rel=icon][type=image/x-icon] with new href
19
- .keywords(['some', 'keyword']) // adding/updating meta[name=keywords]. not adding existing keywords
20
- .image('/main-image.png', '1.5') // adding/updating meta - og:image, twitter:image, twitter:card
21
- .description('Cool site') // adding/updating meta og:description, twitter:description, and description tag
22
- .url('/some', 'http://site.com') // adding/updating meta[og:url] and link[rel="canonical"]
23
- .style([{body:{m:0, bgc:'whitesmoke'}}]) // adding as simple-css styles to existing/new style tag
24
- .style('body {margin:0; background-color:whitesmoke;}', true) // adding css styles to existing/new style tag. Second parameter is minified (default=false).
25
- .link('/styles.css') // adding link[rel=stylesheet] if such href not exists
26
- .script({src:'/app.js'}, '', true) // set script with src to head if such src not exists
27
- .script({}, 'console.log("hello world")', false) // set script with script code to footer
28
-
29
- // Accessors for document parts
30
- layout.body // getter for body element (if not exists, created)
31
- layout.head // getter for head element (if not exists, created)
32
- layout.html // getter for html Element (if not exists, created)
33
-
34
- // Outputs
35
- layout.rawHtml // raw HTML of the document
36
- layout.clone // creates a new layout object clone for current object
37
- ```
38
-
39
- ### onload
40
-
41
- By adding onload attribute, you can run scripts for each element after dom content has loaded.
42
-
43
- Example how it works:
44
- ```js
45
- const layout = new Layout().viewport().title('On load').onload()
46
- layout.body.innerHTML = /*html*/`<div onload="this.innerHTML = 'new content'">original content</div>`
47
- ```
package/docs/2-cloning.md DELETED
@@ -1,19 +0,0 @@
1
- ## Cloning Functionality
2
-
3
- ### What is Cloning and Why is it Necessary?
4
- 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.
5
-
6
- ### How to Use Cloning
7
- 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.
8
-
9
- ```js
10
- const newLayout = layout.clone;
11
- ```
12
-
13
- ### Benefits of Cloning
14
- - **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.
15
- - **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.
16
- - **Isolation:** Changes made to a cloned `Layout` do not affect the original, ensuring that each instance can be modified independently based on specific requirements.
17
-
18
- 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.
19
-
@@ -1,41 +0,0 @@
1
- ## Advanced Usage
2
-
3
- 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:
4
-
5
- ```js
6
- const Layout = require('als-layout')
7
-
8
- // Starting with a basic HTML template and specifying the host for URL methods
9
- const raw = /*html*/`<html></html>`
10
- const host = 'http://example.com';
11
- const layout = new Layout(raw, host, minified=false).lang('fr')
12
- console.log(layout.rawHtml)
13
- // <!DOCTYPE html><html lang="fr"><head></p></head><body></body></html>
14
-
15
- // Cloning the initial layout to create a specialized page
16
- const homePage = layout.clone
17
- homeAutoReload = layout.clone
18
- homePage.title('Home page')
19
- homePage.body.innerHTML = /*html*/`<h1>Home page</h1>`
20
- console.log(homePage.rawHtml)
21
- // <!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>
22
-
23
- // Adding script that reloads the page every minute
24
- homeAutoReload.script({}, 'setTimeout(function() { window.location.reload(); }, 60000);', false)
25
- console.log(homeAutoReload.rawHtml)
26
- // <!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>
27
-
28
- // Demonstrating dynamic stylesheet linkage with versioning
29
- homePage.link('/css/main.css')
30
- console.log(homePage.rawHtml)
31
- // Includes link to the stylesheet with version parameter to ensure fresh cache
32
- ```
33
-
34
- In this example:
35
- - We start with a basic HTML template and use the `lang` method to set the language.
36
- - 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.
37
- - We manipulate the `body` of the `homePage` to include custom HTML.
38
- - We add a script to `homeAutoReload` that sets up an automatic page reload, showcasing how to insert JavaScript dynamically.
39
- - We dynamically add a versioned link to a stylesheet in the `homePage`, demonstrating control over caching and resource management.
40
-
41
- 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.
package/docs/4-view.md DELETED
@@ -1,19 +0,0 @@
1
- ## View method
2
-
3
- **The view method is a pilot. Don't use in production.**
4
-
5
- ```js
6
- Layout.viewsPath = '../../views'; // relative to layout path
7
- Layout.publicPath = '../../public'; // public folder path for urls starts with "/" should be relative to root
8
- Layout.dev = false; // true by default
9
-
10
- const data = {};
11
- const includeBundle = false; // true by default
12
- layout.view('some/Test',data,includeBundle) // viewsPath/some/Test.js
13
- layout.view('some/test',data,includeBundle) // viewsPath/some/test/Index.js
14
-
15
- ```
16
-
17
- * `includeBundle`
18
- * in dev mode, will include bundle for rendering inside script tag in html
19
- * in prod mode, will save context and bundle as files in publicPath/js folder and will add script[src] to html for those files.
@@ -1,72 +0,0 @@
1
- const assert = require('assert');
2
- const { describe, it, beforeEach, before, afterEach } = require('node:test')
3
- const Layout = require('../index');
4
- const { readdirSync, removeSync, existsSync, rmdirSync } = require('fs-extra')
5
- const { join } = require('path')
6
- Layout.viewsPath = 'views';
7
- Layout.publicPath = join(__dirname,'public')
8
-
9
- describe('Dev mode', () => {
10
- let layout
11
- before(() => {
12
- Layout.dev = true;
13
- })
14
- beforeEach(() => {
15
- layout = new Layout()
16
- })
17
-
18
- it('view path without bundle', () => {
19
- const html = layout.view('App', { test: 'test' })
20
- assert(html.includes(/*html*/`<div component="App">Hello test</div>`))
21
- assert(!html.includes(/*html*/`<content></content>`))
22
- })
23
-
24
- it('view Index path without bundle', () => {
25
- const html = layout.view('app', { test: 'test' })
26
- assert(html.includes(/*html*/`<div component="Index">Hello test</div>`))
27
- assert(!html.includes(/*html*/`<content></content>`))
28
- })
29
-
30
- it('view path with bundle', () => {
31
- const html = layout.view('App', { test: 'test' }, true)
32
- assert(html.includes('const Context'))
33
- assert(html.includes('renderedBundle'))
34
- })
35
-
36
- })
37
-
38
- describe('Prod mode', () => {
39
- let layout
40
- const publicPath = join(Layout.publicPath,'js')
41
- before(() => {
42
- Layout.dev = false;
43
- })
44
- beforeEach(() => {
45
- layout = new Layout()
46
- })
47
-
48
- afterEach(() => {
49
- if(!existsSync(publicPath)) return
50
- const files = readdirSync(publicPath)
51
- files.forEach(file => removeSync(join(publicPath,file)))
52
- if(existsSync(publicPath)) rmdirSync(publicPath)
53
- })
54
-
55
- it('view path without bundle', () => {
56
- const html = layout.view('App', { test: 'test' })
57
- assert(html.includes(/*html*/`<div component="App">Hello test</div>`))
58
- assert(!html.includes(/*html*/`<content></content>`))
59
- assert(existsSync(publicPath) === false)
60
- })
61
-
62
- it('view path with bundle', () => {
63
- const html = layout.view('App', { test: 'test' }, true)
64
- const files = readdirSync(publicPath)
65
- assert.deepStrictEqual(files,[ 'App.js', 'context.js' ])
66
- assert(html.includes('/js/App.js'))
67
- assert(html.includes('renderedBundle'))
68
- // <script src="/js/App.js"></script><script>renderedBundle({"test":"test"})</script>
69
- })
70
-
71
- })
72
-
@@ -1,5 +0,0 @@
1
- function App(props={}) {
2
- return (<div>Hello {props.test}</div>)
3
- }
4
-
5
- module.exports = App
@@ -1,5 +0,0 @@
1
- function Index(props={}) {
2
- return (<div>Hello {props.test}</div>)
3
- }
4
-
5
- module.exports = Index