boxwood 0.74.1 → 0.75.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/LICENSE +1 -1
- package/README.md +111 -0
- package/benchmark/index.js +6 -12
- package/index.js +19 -8
- package/package.json +2 -4
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -5,9 +5,28 @@
|
|
|
5
5
|
|
|
6
6
|
> Server side templating engine written in JavaScript
|
|
7
7
|
|
|
8
|
+
[boxwood](https://github.com/buxlabs/boxwood) was created to achieve the following design goals:
|
|
9
|
+
|
|
10
|
+
1. templates can be split into components
|
|
11
|
+
2. css is hashed per component
|
|
12
|
+
3. css is automatically minified
|
|
13
|
+
4. critical css is inlined
|
|
14
|
+
5. templates can import other dependencies
|
|
15
|
+
6. inline images or svgs
|
|
16
|
+
7. i18n support
|
|
17
|
+
8. server side
|
|
18
|
+
9. good for seo
|
|
19
|
+
10. small (1 file, 700 LOC~)
|
|
20
|
+
11. easy to start, familiar syntax
|
|
21
|
+
12. easy to test
|
|
22
|
+
|
|
23
|
+
The template starts with a standard js file, which builds a tree of nodes, that get rendered to html.
|
|
24
|
+
|
|
8
25
|
## Table of Contents
|
|
9
26
|
|
|
10
27
|
- [Install](#install)
|
|
28
|
+
- [Usage](#usage)
|
|
29
|
+
- [Syntax](#syntax)
|
|
11
30
|
- [Maintainers](#maintainers)
|
|
12
31
|
- [Contributing](#contributing)
|
|
13
32
|
- [License](#license)
|
|
@@ -16,6 +35,98 @@
|
|
|
16
35
|
|
|
17
36
|
`npm install boxwood`
|
|
18
37
|
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
const { compile } = require("boxwood")
|
|
42
|
+
const { join } = require("path")
|
|
43
|
+
// ...
|
|
44
|
+
const path = join(__dirname, "index.js")
|
|
45
|
+
const { template } = await compile(path)
|
|
46
|
+
// ...
|
|
47
|
+
const html = template({ foo: "bar" })
|
|
48
|
+
console.log(html)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Syntax
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
// example/index.js
|
|
55
|
+
const layout = require("./layout")
|
|
56
|
+
const banner = require("./banner")
|
|
57
|
+
|
|
58
|
+
module.exports = () => {
|
|
59
|
+
return layout([
|
|
60
|
+
banner({
|
|
61
|
+
title: "Hello, world!",
|
|
62
|
+
description: "Lorem ipsum dolor sit amet",
|
|
63
|
+
}),
|
|
64
|
+
])
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
// example/layout/index.js
|
|
70
|
+
|
|
71
|
+
const { component, css, html, head, body } = require("boxwood")
|
|
72
|
+
const head = require("./head")
|
|
73
|
+
|
|
74
|
+
const styles = css.load(__dirname)
|
|
75
|
+
|
|
76
|
+
module.exports = component(
|
|
77
|
+
(children) => {
|
|
78
|
+
return html([head(), body({ className: styles.layout }, children)])
|
|
79
|
+
},
|
|
80
|
+
{ styles }
|
|
81
|
+
)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
// example/head/index.js
|
|
86
|
+
const { head, title } = require("boxwood")
|
|
87
|
+
|
|
88
|
+
module.exports = () => {
|
|
89
|
+
return head([title("example")])
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
// example/banner/index.js
|
|
95
|
+
const { component, css, h1, p, section } = require("boxwood")
|
|
96
|
+
|
|
97
|
+
const styles = css.load(__dirname)
|
|
98
|
+
|
|
99
|
+
module.exports = component(
|
|
100
|
+
({ title, description }) => {
|
|
101
|
+
return section({ className: styles.banner }, [
|
|
102
|
+
h1(title),
|
|
103
|
+
description && p(description),
|
|
104
|
+
])
|
|
105
|
+
},
|
|
106
|
+
{ styles }
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
// example/banner/index.test.js
|
|
112
|
+
const test = require("node:test")
|
|
113
|
+
const assert = require("node:assert")
|
|
114
|
+
const { compile } = require("boxwood")
|
|
115
|
+
|
|
116
|
+
test("banner renders a title", () => {
|
|
117
|
+
const { template } = await compile(__dirname)
|
|
118
|
+
const html = template({ title: 'foo' })
|
|
119
|
+
assert(html.includes('<h1>foo</h1>'))
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test('banner renders an optional description', () => {
|
|
123
|
+
const { template } = await compile(__dirname)
|
|
124
|
+
const html = template({ title: 'foo', description: 'bar' })
|
|
125
|
+
assert(html.includes('<h1>foo</h1>'))
|
|
126
|
+
assert(html.includes('<p>bar</p>'))
|
|
127
|
+
})
|
|
128
|
+
```
|
|
129
|
+
|
|
19
130
|
## Maintainers
|
|
20
131
|
|
|
21
132
|
[@emilos](https://github.com/emilos)
|
package/benchmark/index.js
CHANGED
|
@@ -6,7 +6,6 @@ const {
|
|
|
6
6
|
} = require("fs")
|
|
7
7
|
const { Suite } = require("benchmark")
|
|
8
8
|
const underscore = require("underscore")
|
|
9
|
-
const template = require("lodash.template")
|
|
10
9
|
const handlebars = require("handlebars")
|
|
11
10
|
const mustache = require("mustache")
|
|
12
11
|
const { compile } = require("..")
|
|
@@ -34,10 +33,9 @@ async function benchmark(dir) {
|
|
|
34
33
|
path.join(__dirname, `./fixtures/${dir}/boxwood.js`)
|
|
35
34
|
)
|
|
36
35
|
const fn2 = underscore.template(source2)
|
|
37
|
-
const fn3 =
|
|
38
|
-
const fn4 =
|
|
39
|
-
const fn5 = (
|
|
40
|
-
const fn6 = require(path.join(__dirname, `./fixtures/${dir}/vanilla.js`))
|
|
36
|
+
const fn3 = handlebars.compile(source4)
|
|
37
|
+
const fn4 = (data) => mustache.render(source5, data)
|
|
38
|
+
const fn5 = require(path.join(__dirname, `./fixtures/${dir}/vanilla.js`))
|
|
41
39
|
mustache.parse(source5)
|
|
42
40
|
|
|
43
41
|
const data = require(path.join(__dirname, `./fixtures/${dir}/data.json`))
|
|
@@ -52,12 +50,11 @@ async function benchmark(dir) {
|
|
|
52
50
|
assert.deepEqual(result, normalize(fn3(data)))
|
|
53
51
|
assert.deepEqual(result, normalize(fn4(data)))
|
|
54
52
|
assert.deepEqual(result, normalize(fn5(data)))
|
|
55
|
-
assert.deepEqual(result, normalize(fn6(data)))
|
|
56
53
|
|
|
57
54
|
await new Promise((resolve) => {
|
|
58
55
|
suite
|
|
59
56
|
.add("vanilla[js]", function () {
|
|
60
|
-
|
|
57
|
+
fn5(data)
|
|
61
58
|
})
|
|
62
59
|
.add("boxwood[js]", function () {
|
|
63
60
|
fn1(data)
|
|
@@ -65,14 +62,11 @@ async function benchmark(dir) {
|
|
|
65
62
|
.add("underscore[ejs]", function () {
|
|
66
63
|
fn2(data)
|
|
67
64
|
})
|
|
68
|
-
.add("lodash[ejs]", function () {
|
|
69
|
-
fn3(data)
|
|
70
|
-
})
|
|
71
65
|
.add("handlebars[hbs]", function () {
|
|
72
|
-
|
|
66
|
+
fn3(data)
|
|
73
67
|
})
|
|
74
68
|
.add("mustache[mst]", function () {
|
|
75
|
-
|
|
69
|
+
fn4(data)
|
|
76
70
|
})
|
|
77
71
|
.on("cycle", function (event) {
|
|
78
72
|
console.log(`${dir}: ${String(event.target)}`)
|
package/index.js
CHANGED
|
@@ -314,9 +314,20 @@ function js(inputs) {
|
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
js.load = function () {
|
|
317
|
-
const
|
|
317
|
+
const parts = []
|
|
318
|
+
for (const param of arguments) {
|
|
319
|
+
if (typeof param === "string") {
|
|
320
|
+
parts.push(param)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const path = join(...parts)
|
|
318
324
|
const file = path.endsWith(".js") ? path : join(path, "index.js")
|
|
319
325
|
const content = readFileSync(file, "utf8")
|
|
326
|
+
|
|
327
|
+
const options = arguments[arguments.length - 1]
|
|
328
|
+
if (options && options.transform) {
|
|
329
|
+
return js`${options.transform(content)}`
|
|
330
|
+
}
|
|
320
331
|
return js`${content}`
|
|
321
332
|
}
|
|
322
333
|
|
|
@@ -539,7 +550,7 @@ i18n.load = function () {
|
|
|
539
550
|
}
|
|
540
551
|
}
|
|
541
552
|
|
|
542
|
-
function component(fn, { styles, i18n,
|
|
553
|
+
function component(fn, { styles, i18n, scripts } = {}) {
|
|
543
554
|
function execute(a, b) {
|
|
544
555
|
if (typeof a === "string" || typeof a === "number" || Array.isArray(a)) {
|
|
545
556
|
return fn({}, a)
|
|
@@ -571,11 +582,11 @@ function component(fn, { styles, i18n, code } = {}) {
|
|
|
571
582
|
}
|
|
572
583
|
return function (a, b) {
|
|
573
584
|
const tree = execute(a, b)
|
|
574
|
-
if (styles &&
|
|
585
|
+
if (styles && scripts) {
|
|
575
586
|
if (Array.isArray(tree)) {
|
|
576
|
-
return tree.concat(styles.css,
|
|
587
|
+
return tree.concat(styles.css, scripts.js)
|
|
577
588
|
}
|
|
578
|
-
return [tree, styles.css,
|
|
589
|
+
return [tree, styles.css, scripts.js]
|
|
579
590
|
}
|
|
580
591
|
if (styles) {
|
|
581
592
|
if (Array.isArray(tree)) {
|
|
@@ -583,11 +594,11 @@ function component(fn, { styles, i18n, code } = {}) {
|
|
|
583
594
|
}
|
|
584
595
|
return [tree, styles.css]
|
|
585
596
|
}
|
|
586
|
-
if (
|
|
597
|
+
if (scripts) {
|
|
587
598
|
if (Array.isArray(tree)) {
|
|
588
|
-
return tree.concat(
|
|
599
|
+
return tree.concat(scripts.js)
|
|
589
600
|
}
|
|
590
|
-
return [tree,
|
|
601
|
+
return [tree, scripts.js]
|
|
591
602
|
}
|
|
592
603
|
return tree
|
|
593
604
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "boxwood",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.75.0",
|
|
4
4
|
"description": "Compile HTML templates into JS",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -45,13 +45,11 @@
|
|
|
45
45
|
},
|
|
46
46
|
"homepage": "https://github.com/buxlabs/boxwood#readme",
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"ava": "^6.1.2",
|
|
49
48
|
"benchmark": "2.1.4",
|
|
50
49
|
"c8": "^9.1.0",
|
|
51
50
|
"express": "^4.19.2",
|
|
52
51
|
"handlebars": "^4.7.8",
|
|
53
52
|
"jsdom": "^24.0.0",
|
|
54
|
-
"lodash.template": "4.5.0",
|
|
55
53
|
"mustache": "^4.2.0",
|
|
56
54
|
"underscore": "^1.13.6"
|
|
57
55
|
},
|
|
@@ -65,6 +63,6 @@
|
|
|
65
63
|
"dependencies": {
|
|
66
64
|
"css-tree": "^2.3.1",
|
|
67
65
|
"string-hash": "^1.1.3",
|
|
68
|
-
"yaml": "^2.4.
|
|
66
|
+
"yaml": "^2.4.2"
|
|
69
67
|
}
|
|
70
68
|
}
|