als-layout 7.0.0 → 7.1.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/index.mjs +89 -81
- package/layout.js +89 -81
- package/package.json +16 -4
- package/readme.md +186 -163
- package/src/layout.js +93 -85
- package/build.js +0 -8
- package/tests/constructor.test.js +0 -28
- package/tests/description.test.js +0 -33
- package/tests/favicon.test.js +0 -36
- package/tests/image.test.js +0 -25
- package/tests/integrative.test.js +0 -83
- package/tests/keywords.test.js +0 -47
- package/tests/link.test.js +0 -33
- package/tests/script.test.js +0 -42
- package/tests/style.test.js +0 -34
- package/tests/url.test.js +0 -42
- package/tests/viewport.test.js +0 -41
package/index.mjs
CHANGED
|
@@ -1,83 +1,91 @@
|
|
|
1
1
|
import { Document, SingleNode, Node } from 'als-document';
|
|
2
|
-
export class Layout extends Document {
|
|
3
|
-
constructor(html, host) { super(html, host); this.root = this.html; }
|
|
4
|
-
get rawHtml() { return this.innerHTML }
|
|
5
|
-
get clone() { return new this.constructor(new Document(this, this.URL), this.
|
|
6
|
-
lang(lang) { this.html.setAttribute('lang', lang); return this }
|
|
7
|
-
link(href, attributes = { rel: "stylesheet", type: "text/css" }) {
|
|
8
|
-
if (!href || typeof href !== 'string') throw new Error(`href attribute must be a string`)
|
|
9
|
-
if (!this.root.querySelector(`link[rel=stylesheet][href^="${href}"]`))
|
|
10
|
-
this.head.insert(2, new SingleNode('link', { href, ...attributes }))
|
|
11
|
-
return this
|
|
12
|
-
}
|
|
13
|
-
keywords(keywords = []) {
|
|
14
|
-
let el = this.root.$('meta[name=keywords]') || new SingleNode('meta', { name: 'keywords' })
|
|
15
|
-
keywords = new Set([...(el.getAttribute('content') || '').split(','),...keywords.map(k => k.trim())].filter(Boolean))
|
|
16
|
-
if (keywords.size) el.setAttribute('content', Array.from(keywords).join(','))
|
|
17
|
-
if(keywords.size && !el.parent) this.head.insert(2, el)
|
|
18
|
-
return this
|
|
19
|
-
}
|
|
20
|
-
style(styles) {
|
|
21
|
-
if (typeof styles !== 'string') throw 'styles parameter should be string';
|
|
22
|
-
let el = this.root.$('style') || this.head.insert(2, new Node('style'))
|
|
23
|
-
el.innerHTML = el.innerHTML + '\n' + styles;
|
|
24
|
-
return this
|
|
25
|
-
}
|
|
26
|
-
url(url, host = this.URL) {
|
|
27
|
-
try {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
el.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (attrs
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
this.meta({
|
|
56
|
-
this.meta({ property: '
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
this.meta({
|
|
74
|
-
this.meta({ name: 'twitter:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
super.title
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
2
|
+
export class Layout extends Document {
|
|
3
|
+
constructor(html, host) { super(html, host); this.root = this.html; }
|
|
4
|
+
get rawHtml() { return this.innerHTML }
|
|
5
|
+
get clone() { return new this.constructor(new Document(this, this.URL), this.URL) }
|
|
6
|
+
lang(lang) { this.html.setAttribute('lang', lang); return this }
|
|
7
|
+
link(href, attributes = { rel: "stylesheet", type: "text/css" }) {
|
|
8
|
+
if (!href || typeof href !== 'string') throw new Error(`href attribute must be a string`)
|
|
9
|
+
if (!this.root.querySelector(`link[rel=stylesheet][href^="${href}"]`))
|
|
10
|
+
this.head.insert(2, new SingleNode('link', { href, ...attributes }))
|
|
11
|
+
return this
|
|
12
|
+
}
|
|
13
|
+
keywords(keywords = []) {
|
|
14
|
+
let el = this.root.$('meta[name=keywords]') || new SingleNode('meta', { name: 'keywords' })
|
|
15
|
+
keywords = new Set([...(el.getAttribute('content') || '').split(','),...keywords.map(k => k.trim())].filter(Boolean))
|
|
16
|
+
if (keywords.size) el.setAttribute('content', Array.from(keywords).join(','))
|
|
17
|
+
if(keywords.size && !el.parent) this.head.insert(2, el)
|
|
18
|
+
return this
|
|
19
|
+
}
|
|
20
|
+
style(styles) {
|
|
21
|
+
if (typeof styles !== 'string') throw 'styles parameter should be string';
|
|
22
|
+
let el = this.root.$('style') || this.head.insert(2, new Node('style'))
|
|
23
|
+
el.innerHTML = el.innerHTML + '\n' + styles;
|
|
24
|
+
return this
|
|
25
|
+
}
|
|
26
|
+
url(url, host = this.URL) {
|
|
27
|
+
try {
|
|
28
|
+
this.urlObj = (host ? new URL(url, host) : new URL(url))
|
|
29
|
+
url = this.urlObj.href
|
|
30
|
+
this.meta({ property: 'og:url', content: url })
|
|
31
|
+
const el = this.root.$('link[rel="canonical"]') || this.head.insert(2, new SingleNode('link', { rel: 'canonical', href: url }))
|
|
32
|
+
el.setAttribute('href', url)
|
|
33
|
+
} catch (error) { error.info = `url "${url}" with host "${host}" is not valid url`; throw error; }
|
|
34
|
+
return this
|
|
35
|
+
}
|
|
36
|
+
meta(props) {
|
|
37
|
+
const entries = Object.entries(props)
|
|
38
|
+
const [name, value] = entries[0]
|
|
39
|
+
const metaElement = this.root.querySelector(`meta[${name}="${value}"]`) || this.head.insert(2, new SingleNode('meta', props))
|
|
40
|
+
entries.forEach(([name, v]) => metaElement.setAttribute(name, props[name]))
|
|
41
|
+
return this
|
|
42
|
+
}
|
|
43
|
+
script(attrs = {}, innerHTML = '', head = true) {
|
|
44
|
+
if (typeof attrs !== 'object' || attrs === null || Array.isArray(attrs)) attrs = {}
|
|
45
|
+
if (attrs.src && this.root.querySelector(`script[src="${attrs.src}"]`)) return this
|
|
46
|
+
if (Object.keys(attrs).length || innerHTML) {
|
|
47
|
+
const script = new Node('script', attrs)
|
|
48
|
+
if (innerHTML) script.innerHTML = innerHTML
|
|
49
|
+
if (head) this.head.insert(2, script)
|
|
50
|
+
else this.html.insert(2, script)
|
|
51
|
+
}
|
|
52
|
+
return this
|
|
53
|
+
}
|
|
54
|
+
description(description) {
|
|
55
|
+
this.meta({ name: 'description', content: description })
|
|
56
|
+
this.meta({ property: 'og:description', content: description })
|
|
57
|
+
this.meta({ name: 'twitter:description', content: description })
|
|
58
|
+
return this
|
|
59
|
+
}
|
|
60
|
+
favicon(href) {
|
|
61
|
+
const el = this.root.$('link[rel=icon][type=image/x-icon]')
|
|
62
|
+
if (el) el.setAttribute('href', href)
|
|
63
|
+
else this.head.insert(2, new SingleNode('link', { rel: 'icon', href, type: 'image/x-icon' }))
|
|
64
|
+
return this
|
|
65
|
+
}
|
|
66
|
+
viewport(viewport = 'width=device-width, initial-scale=1.0') {
|
|
67
|
+
const el = this.root.$('meta[name="viewport"]')
|
|
68
|
+
if (el) el.setAttribute('content', viewport)
|
|
69
|
+
else this.head.insert(2, new SingleNode('meta', { name: 'viewport', content: viewport }))
|
|
70
|
+
return this
|
|
71
|
+
}
|
|
72
|
+
image(image) {
|
|
73
|
+
this.meta({ property: 'og:image', content: image })
|
|
74
|
+
this.meta({ name: 'twitter:image', content: image })
|
|
75
|
+
this.meta({ name: 'twitter:card', content: 'summary_large_image' })
|
|
76
|
+
return this
|
|
77
|
+
}
|
|
78
|
+
title(title) {
|
|
79
|
+
super.title // create title tag if not exists
|
|
80
|
+
super.title = title
|
|
81
|
+
this.meta({ property: 'og:title', content: title })
|
|
82
|
+
return this
|
|
83
|
+
}
|
|
84
|
+
toDocument() {
|
|
85
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') return this
|
|
86
|
+
document.querySelector('title')?.remove()
|
|
87
|
+
document.head.innerHTML = this.head.innerHTML
|
|
88
|
+
document.body.outerHTML = this.body.outerHTML
|
|
89
|
+
return this
|
|
90
|
+
}
|
|
83
91
|
}
|
package/layout.js
CHANGED
|
@@ -43,85 +43,93 @@ function cacheDoc(doc){
|
|
|
43
43
|
const props=['isSingle','tagName','attributes']
|
|
44
44
|
fu
|
|
45
45
|
return { parseHTML,Node,Query,TextNode,SingleNode,buildFromCache,cacheDoc,Root,Document }
|
|
46
46
|
})()
|
|
47
47
|
const { Document, SingleNode, Node } = alsDocument
|
|
48
|
-
class Layout extends Document {
|
|
49
|
-
constructor(html, host) { super(html, host); this.root = this.html; }
|
|
50
|
-
get rawHtml() { return this.innerHTML }
|
|
51
|
-
get clone() { return new this.constructor(new Document(this, this.URL), this.
|
|
52
|
-
lang(lang) { this.html.setAttribute('lang', lang); return this }
|
|
53
|
-
link(href, attributes = { rel: "stylesheet", type: "text/css" }) {
|
|
54
|
-
if (!href || typeof href !== 'string') throw new Error(`href attribute must be a string`)
|
|
55
|
-
if (!this.root.querySelector(`link[rel=stylesheet][href^="${href}"]`))
|
|
56
|
-
this.head.insert(2, new SingleNode('link', { href, ...attributes }))
|
|
57
|
-
return this
|
|
58
|
-
}
|
|
59
|
-
keywords(keywords = []) {
|
|
60
|
-
let el = this.root.$('meta[name=keywords]') || new SingleNode('meta', { name: 'keywords' })
|
|
61
|
-
keywords = new Set([...(el.getAttribute('content') || '').split(','),...keywords.map(k => k.trim())].filter(Boolean))
|
|
62
|
-
if (keywords.size) el.setAttribute('content', Array.from(keywords).join(','))
|
|
63
|
-
if(keywords.size && !el.parent) this.head.insert(2, el)
|
|
64
|
-
return this
|
|
65
|
-
}
|
|
66
|
-
style(styles) {
|
|
67
|
-
if (typeof styles !== 'string') throw 'styles parameter should be string';
|
|
68
|
-
let el = this.root.$('style') || this.head.insert(2, new Node('style'))
|
|
69
|
-
el.innerHTML = el.innerHTML + '\n' + styles;
|
|
70
|
-
return this
|
|
71
|
-
}
|
|
72
|
-
url(url, host = this.URL) {
|
|
73
|
-
try {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
el.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (attrs
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
this.meta({
|
|
102
|
-
this.meta({ property: '
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
this.meta({
|
|
120
|
-
this.meta({ name: 'twitter:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
super.title
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
48
|
+
class Layout extends Document {
|
|
49
|
+
constructor(html, host) { super(html, host); this.root = this.html; }
|
|
50
|
+
get rawHtml() { return this.innerHTML }
|
|
51
|
+
get clone() { return new this.constructor(new Document(this, this.URL), this.URL) }
|
|
52
|
+
lang(lang) { this.html.setAttribute('lang', lang); return this }
|
|
53
|
+
link(href, attributes = { rel: "stylesheet", type: "text/css" }) {
|
|
54
|
+
if (!href || typeof href !== 'string') throw new Error(`href attribute must be a string`)
|
|
55
|
+
if (!this.root.querySelector(`link[rel=stylesheet][href^="${href}"]`))
|
|
56
|
+
this.head.insert(2, new SingleNode('link', { href, ...attributes }))
|
|
57
|
+
return this
|
|
58
|
+
}
|
|
59
|
+
keywords(keywords = []) {
|
|
60
|
+
let el = this.root.$('meta[name=keywords]') || new SingleNode('meta', { name: 'keywords' })
|
|
61
|
+
keywords = new Set([...(el.getAttribute('content') || '').split(','),...keywords.map(k => k.trim())].filter(Boolean))
|
|
62
|
+
if (keywords.size) el.setAttribute('content', Array.from(keywords).join(','))
|
|
63
|
+
if(keywords.size && !el.parent) this.head.insert(2, el)
|
|
64
|
+
return this
|
|
65
|
+
}
|
|
66
|
+
style(styles) {
|
|
67
|
+
if (typeof styles !== 'string') throw 'styles parameter should be string';
|
|
68
|
+
let el = this.root.$('style') || this.head.insert(2, new Node('style'))
|
|
69
|
+
el.innerHTML = el.innerHTML + '\n' + styles;
|
|
70
|
+
return this
|
|
71
|
+
}
|
|
72
|
+
url(url, host = this.URL) {
|
|
73
|
+
try {
|
|
74
|
+
this.urlObj = (host ? new URL(url, host) : new URL(url))
|
|
75
|
+
url = this.urlObj.href
|
|
76
|
+
this.meta({ property: 'og:url', content: url })
|
|
77
|
+
const el = this.root.$('link[rel="canonical"]') || this.head.insert(2, new SingleNode('link', { rel: 'canonical', href: url }))
|
|
78
|
+
el.setAttribute('href', url)
|
|
79
|
+
} catch (error) { error.info = `url "${url}" with host "${host}" is not valid url`; throw error; }
|
|
80
|
+
return this
|
|
81
|
+
}
|
|
82
|
+
meta(props) {
|
|
83
|
+
const entries = Object.entries(props)
|
|
84
|
+
const [name, value] = entries[0]
|
|
85
|
+
const metaElement = this.root.querySelector(`meta[${name}="${value}"]`) || this.head.insert(2, new SingleNode('meta', props))
|
|
86
|
+
entries.forEach(([name, v]) => metaElement.setAttribute(name, props[name]))
|
|
87
|
+
return this
|
|
88
|
+
}
|
|
89
|
+
script(attrs = {}, innerHTML = '', head = true) {
|
|
90
|
+
if (typeof attrs !== 'object' || attrs === null || Array.isArray(attrs)) attrs = {}
|
|
91
|
+
if (attrs.src && this.root.querySelector(`script[src="${attrs.src}"]`)) return this
|
|
92
|
+
if (Object.keys(attrs).length || innerHTML) {
|
|
93
|
+
const script = new Node('script', attrs)
|
|
94
|
+
if (innerHTML) script.innerHTML = innerHTML
|
|
95
|
+
if (head) this.head.insert(2, script)
|
|
96
|
+
else this.html.insert(2, script)
|
|
97
|
+
}
|
|
98
|
+
return this
|
|
99
|
+
}
|
|
100
|
+
description(description) {
|
|
101
|
+
this.meta({ name: 'description', content: description })
|
|
102
|
+
this.meta({ property: 'og:description', content: description })
|
|
103
|
+
this.meta({ name: 'twitter:description', content: description })
|
|
104
|
+
return this
|
|
105
|
+
}
|
|
106
|
+
favicon(href) {
|
|
107
|
+
const el = this.root.$('link[rel=icon][type=image/x-icon]')
|
|
108
|
+
if (el) el.setAttribute('href', href)
|
|
109
|
+
else this.head.insert(2, new SingleNode('link', { rel: 'icon', href, type: 'image/x-icon' }))
|
|
110
|
+
return this
|
|
111
|
+
}
|
|
112
|
+
viewport(viewport = 'width=device-width, initial-scale=1.0') {
|
|
113
|
+
const el = this.root.$('meta[name="viewport"]')
|
|
114
|
+
if (el) el.setAttribute('content', viewport)
|
|
115
|
+
else this.head.insert(2, new SingleNode('meta', { name: 'viewport', content: viewport }))
|
|
116
|
+
return this
|
|
117
|
+
}
|
|
118
|
+
image(image) {
|
|
119
|
+
this.meta({ property: 'og:image', content: image })
|
|
120
|
+
this.meta({ name: 'twitter:image', content: image })
|
|
121
|
+
this.meta({ name: 'twitter:card', content: 'summary_large_image' })
|
|
122
|
+
return this
|
|
123
|
+
}
|
|
124
|
+
title(title) {
|
|
125
|
+
super.title // create title tag if not exists
|
|
126
|
+
super.title = title
|
|
127
|
+
this.meta({ property: 'og:title', content: title })
|
|
128
|
+
return this
|
|
129
|
+
}
|
|
130
|
+
toDocument() {
|
|
131
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') return this
|
|
132
|
+
document.querySelector('title')?.remove()
|
|
133
|
+
document.head.innerHTML = this.head.innerHTML
|
|
134
|
+
document.body.outerHTML = this.body.outerHTML
|
|
135
|
+
return this
|
|
136
|
+
}
|
|
129
137
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "als-layout",
|
|
3
|
-
"version": "7.
|
|
4
|
-
"main": "./
|
|
3
|
+
"version": "7.1.0",
|
|
4
|
+
"main": "./src/layout.js",
|
|
5
5
|
"module": "./index.mjs",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"import": "./index.mjs",
|
|
9
|
+
"require": "./src/layout.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"index.mjs",
|
|
14
|
+
"layout.js",
|
|
15
|
+
"src/layout.js",
|
|
16
|
+
"readme.md"
|
|
17
|
+
],
|
|
6
18
|
"scripts": {
|
|
19
|
+
"build": "node build.js",
|
|
7
20
|
"test": "node --test --experimental-test-coverage ./tests/**.*",
|
|
8
|
-
"report": "node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=lcov.info"
|
|
9
|
-
"docs": "node ./scripts/build-readme.js"
|
|
21
|
+
"report": "node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=lcov.info"
|
|
10
22
|
},
|
|
11
23
|
"keywords": [
|
|
12
24
|
"html",
|