boxwood 0.63.1 → 0.65.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.js +228 -210
- package/package.json +3 -3
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const { join } = require(
|
|
2
|
-
const { readFileSync } = require(
|
|
3
|
-
const csstree = require(
|
|
4
|
-
const toHash = require(
|
|
1
|
+
const { join } = require("path")
|
|
2
|
+
const { readFileSync } = require("fs")
|
|
3
|
+
const csstree = require("css-tree")
|
|
4
|
+
const toHash = require("string-hash")
|
|
5
5
|
|
|
6
6
|
async function compile(path) {
|
|
7
7
|
const fn = require(path)
|
|
@@ -12,20 +12,23 @@ async function compile(path) {
|
|
|
12
12
|
const styles = []
|
|
13
13
|
const scripts = []
|
|
14
14
|
const walk = (node) => {
|
|
15
|
-
if (node
|
|
15
|
+
if (!node) {
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
if (node.name === "head") {
|
|
16
19
|
nodes.head = node
|
|
17
20
|
}
|
|
18
|
-
if (node.name ===
|
|
21
|
+
if (node.name === "body") {
|
|
19
22
|
nodes.body = node
|
|
20
23
|
}
|
|
21
|
-
if (node.name ===
|
|
24
|
+
if (node.name === "style") {
|
|
22
25
|
const css = node.children
|
|
23
26
|
if (!styles.includes(css)) {
|
|
24
27
|
styles.push(css)
|
|
25
28
|
}
|
|
26
29
|
node.ignore = true
|
|
27
30
|
}
|
|
28
|
-
if (node.name ===
|
|
31
|
+
if (node.name === "script") {
|
|
29
32
|
const js = node.children
|
|
30
33
|
if (!scripts.includes(js)) {
|
|
31
34
|
scripts.push(js)
|
|
@@ -35,23 +38,23 @@ async function compile(path) {
|
|
|
35
38
|
if (Array.isArray(node)) {
|
|
36
39
|
node.forEach(walk)
|
|
37
40
|
} else if (Array.isArray(node.children)) {
|
|
38
|
-
node.children.forEach(walk)
|
|
41
|
+
node.children.filter(Boolean).forEach(walk)
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
44
|
walk(tree)
|
|
42
45
|
if (nodes.head) {
|
|
43
46
|
if (styles.length > 0) {
|
|
44
47
|
nodes.head.children.push({
|
|
45
|
-
name:
|
|
46
|
-
children: styles.join(
|
|
48
|
+
name: "style",
|
|
49
|
+
children: styles.join(""),
|
|
47
50
|
})
|
|
48
51
|
}
|
|
49
52
|
}
|
|
50
53
|
if (nodes.body) {
|
|
51
54
|
if (scripts.length > 0) {
|
|
52
55
|
nodes.body.children.push({
|
|
53
|
-
name:
|
|
54
|
-
children: scripts.join(
|
|
56
|
+
name: "script",
|
|
57
|
+
children: scripts.join(""),
|
|
55
58
|
})
|
|
56
59
|
}
|
|
57
60
|
}
|
|
@@ -61,11 +64,11 @@ async function compile(path) {
|
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
const ENTITIES = {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"'":
|
|
68
|
-
'"':
|
|
67
|
+
"&": "&",
|
|
68
|
+
"<": "<",
|
|
69
|
+
">": ">",
|
|
70
|
+
"'": "'",
|
|
71
|
+
'"': """,
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
const REGEXP = /[&<>'"]/g
|
|
@@ -77,47 +80,47 @@ const escapeHTML = (string) => {
|
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
const BOOLEAN_ATTRIBUTES = [
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
83
|
+
"async",
|
|
84
|
+
"autofocus",
|
|
85
|
+
"autoplay",
|
|
86
|
+
"border",
|
|
87
|
+
"challenge",
|
|
88
|
+
"checked",
|
|
89
|
+
"compact",
|
|
90
|
+
"contenteditable",
|
|
91
|
+
"controls",
|
|
92
|
+
"default",
|
|
93
|
+
"defer",
|
|
94
|
+
"disabled",
|
|
95
|
+
"formnovalidate",
|
|
96
|
+
"frameborder",
|
|
97
|
+
"hidden",
|
|
98
|
+
"indeterminate",
|
|
99
|
+
"ismap",
|
|
100
|
+
"loop",
|
|
101
|
+
"multiple",
|
|
102
|
+
"muted",
|
|
103
|
+
"nohref",
|
|
104
|
+
"noresize",
|
|
105
|
+
"noshade",
|
|
106
|
+
"novalidate",
|
|
107
|
+
"nowrap",
|
|
108
|
+
"open",
|
|
109
|
+
"readonly",
|
|
110
|
+
"required",
|
|
111
|
+
"reversed",
|
|
112
|
+
"scoped",
|
|
113
|
+
"scrolling",
|
|
114
|
+
"seamless",
|
|
115
|
+
"selected",
|
|
116
|
+
"sortable",
|
|
117
|
+
"spellcheck",
|
|
118
|
+
"translate",
|
|
116
119
|
]
|
|
117
120
|
|
|
118
121
|
const ALIASES = {
|
|
119
|
-
className:
|
|
120
|
-
htmlFor:
|
|
122
|
+
className: "class",
|
|
123
|
+
htmlFor: "for",
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
const attributes = (options) => {
|
|
@@ -129,59 +132,61 @@ const attributes = (options) => {
|
|
|
129
132
|
result.push(key)
|
|
130
133
|
} else {
|
|
131
134
|
const name = ALIASES[key] || key
|
|
132
|
-
|
|
135
|
+
const value = options[key]
|
|
136
|
+
const content = Array.isArray(value) ? classes(...value) : value
|
|
137
|
+
result.push(name + "=" + '"' + content + '"')
|
|
133
138
|
}
|
|
134
139
|
}
|
|
135
140
|
}
|
|
136
|
-
return result.join(
|
|
141
|
+
return result.join(" ")
|
|
137
142
|
}
|
|
138
143
|
|
|
139
144
|
const SELF_CLOSING_TAGS = [
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
145
|
+
"area",
|
|
146
|
+
"base",
|
|
147
|
+
"br",
|
|
148
|
+
"col",
|
|
149
|
+
"command",
|
|
150
|
+
"embed",
|
|
151
|
+
"hr",
|
|
152
|
+
"img",
|
|
153
|
+
"input",
|
|
154
|
+
"keygen",
|
|
155
|
+
"link",
|
|
156
|
+
"meta",
|
|
157
|
+
"param",
|
|
158
|
+
"source",
|
|
159
|
+
"track",
|
|
160
|
+
"wbr",
|
|
161
|
+
"!DOCTYPE html",
|
|
157
162
|
]
|
|
158
163
|
|
|
159
164
|
const isUnescapedTag = (name) => {
|
|
160
|
-
return ![
|
|
165
|
+
return !["script", "style", "template"].includes(name)
|
|
161
166
|
}
|
|
162
167
|
|
|
163
168
|
const render = (input, escape = true) => {
|
|
164
169
|
if (input.ignore) {
|
|
165
|
-
return
|
|
170
|
+
return ""
|
|
166
171
|
}
|
|
167
172
|
if (Array.isArray(input)) {
|
|
168
|
-
return input.filter(Boolean).map(render).join(
|
|
173
|
+
return input.filter(Boolean).map(render).join("")
|
|
169
174
|
}
|
|
170
|
-
if (typeof input ===
|
|
175
|
+
if (typeof input === "number") {
|
|
171
176
|
return input.toString()
|
|
172
177
|
}
|
|
173
|
-
if (typeof input ===
|
|
178
|
+
if (typeof input === "string") {
|
|
174
179
|
return escape ? escapeHTML(input) : input
|
|
175
180
|
}
|
|
176
|
-
if (input.name ===
|
|
181
|
+
if (input.name === "fragment") {
|
|
177
182
|
return render(input.children)
|
|
178
183
|
}
|
|
179
|
-
if (input.name ===
|
|
184
|
+
if (input.name === "raw") {
|
|
180
185
|
return render(input.children, false)
|
|
181
186
|
}
|
|
182
187
|
if (SELF_CLOSING_TAGS.includes(input.name)) {
|
|
183
188
|
if (input.attributes) {
|
|
184
|
-
return `<${input.name} ` + attributes(input.attributes) +
|
|
189
|
+
return `<${input.name} ` + attributes(input.attributes) + ">"
|
|
185
190
|
}
|
|
186
191
|
return `<${input.name}>`
|
|
187
192
|
}
|
|
@@ -189,7 +194,7 @@ const render = (input, escape = true) => {
|
|
|
189
194
|
return (
|
|
190
195
|
`<${input.name} ` +
|
|
191
196
|
attributes(input.attributes) +
|
|
192
|
-
|
|
197
|
+
">" +
|
|
193
198
|
render(input.children, isUnescapedTag(input.name)) +
|
|
194
199
|
`</${input.name}>`
|
|
195
200
|
)
|
|
@@ -200,22 +205,26 @@ const render = (input, escape = true) => {
|
|
|
200
205
|
)
|
|
201
206
|
}
|
|
202
207
|
if (input.children) {
|
|
203
|
-
return
|
|
208
|
+
return (
|
|
209
|
+
`<${input.name}>` +
|
|
210
|
+
render(input.children, isUnescapedTag(input.name)) +
|
|
211
|
+
`</${input.name}>`
|
|
212
|
+
)
|
|
204
213
|
}
|
|
205
214
|
return `<${input.name}></${input.name}>`
|
|
206
215
|
}
|
|
207
216
|
|
|
208
217
|
const fragment = (children) => {
|
|
209
|
-
return { name:
|
|
218
|
+
return { name: "fragment", children }
|
|
210
219
|
}
|
|
211
220
|
|
|
212
221
|
const raw = (children) => {
|
|
213
|
-
return { name:
|
|
222
|
+
return { name: "raw", children }
|
|
214
223
|
}
|
|
215
224
|
|
|
216
225
|
raw.load = function () {
|
|
217
226
|
const path = join(...arguments)
|
|
218
|
-
const content = readFileSync(path,
|
|
227
|
+
const content = readFileSync(path, "utf8")
|
|
219
228
|
return raw(content)
|
|
220
229
|
}
|
|
221
230
|
|
|
@@ -245,7 +254,7 @@ const tag = (a, b, c) => {
|
|
|
245
254
|
}
|
|
246
255
|
|
|
247
256
|
function css(inputs) {
|
|
248
|
-
let result =
|
|
257
|
+
let result = ""
|
|
249
258
|
for (let i = 0, ilen = inputs.length; i < ilen; i += 1) {
|
|
250
259
|
const input = inputs[i]
|
|
251
260
|
const value = arguments[i + 1]
|
|
@@ -260,7 +269,7 @@ function css(inputs) {
|
|
|
260
269
|
const classes = {}
|
|
261
270
|
|
|
262
271
|
csstree.walk(tree, (node) => {
|
|
263
|
-
if (node.type ===
|
|
272
|
+
if (node.type === "ClassSelector") {
|
|
264
273
|
const name = `__${node.name}__${hash}`
|
|
265
274
|
classes[node.name] = name
|
|
266
275
|
node.name = name
|
|
@@ -269,18 +278,20 @@ function css(inputs) {
|
|
|
269
278
|
|
|
270
279
|
return {
|
|
271
280
|
...classes,
|
|
272
|
-
css: tag(
|
|
281
|
+
css: tag("style", csstree.generate(tree)),
|
|
273
282
|
}
|
|
274
283
|
}
|
|
275
284
|
|
|
276
285
|
css.load = function () {
|
|
277
286
|
const path = join(...arguments)
|
|
278
|
-
const content = readFileSync(path,
|
|
279
|
-
return css
|
|
287
|
+
const content = readFileSync(path, "utf8")
|
|
288
|
+
return css`
|
|
289
|
+
${content}
|
|
290
|
+
`
|
|
280
291
|
}
|
|
281
292
|
|
|
282
293
|
function js(inputs) {
|
|
283
|
-
let result =
|
|
294
|
+
let result = ""
|
|
284
295
|
for (let i = 0, ilen = inputs.length; i < ilen; i += 1) {
|
|
285
296
|
const input = inputs[i]
|
|
286
297
|
const value = arguments[i + 1]
|
|
@@ -291,130 +302,130 @@ function js(inputs) {
|
|
|
291
302
|
}
|
|
292
303
|
}
|
|
293
304
|
return {
|
|
294
|
-
js: tag(
|
|
305
|
+
js: tag("script", result),
|
|
295
306
|
}
|
|
296
307
|
}
|
|
297
308
|
|
|
298
309
|
js.load = function () {
|
|
299
310
|
const path = join(...arguments)
|
|
300
|
-
const content = readFileSync(path,
|
|
311
|
+
const content = readFileSync(path, "utf8")
|
|
301
312
|
return js`${content}`
|
|
302
313
|
}
|
|
303
314
|
|
|
304
315
|
const node = (name) => (options, children) => tag(name, options, children)
|
|
305
|
-
const doctype = node(
|
|
316
|
+
const doctype = node("!DOCTYPE html")
|
|
306
317
|
|
|
307
318
|
const nodes = [
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
319
|
+
"a",
|
|
320
|
+
"abbr",
|
|
321
|
+
"address",
|
|
322
|
+
"area",
|
|
323
|
+
"article",
|
|
324
|
+
"aside",
|
|
325
|
+
"audio",
|
|
326
|
+
"b",
|
|
327
|
+
"base",
|
|
328
|
+
"bdi",
|
|
329
|
+
"bdo",
|
|
330
|
+
"blockquote",
|
|
331
|
+
"body",
|
|
332
|
+
"br",
|
|
333
|
+
"button",
|
|
334
|
+
"canvas",
|
|
335
|
+
"caption",
|
|
336
|
+
"cite",
|
|
337
|
+
"code",
|
|
338
|
+
"col",
|
|
339
|
+
"colgroup",
|
|
340
|
+
"data",
|
|
341
|
+
"datalist",
|
|
342
|
+
"dd",
|
|
343
|
+
"del",
|
|
344
|
+
"details",
|
|
345
|
+
"dfn",
|
|
346
|
+
"dialog",
|
|
347
|
+
"div",
|
|
348
|
+
"dl",
|
|
349
|
+
"dt",
|
|
350
|
+
"em",
|
|
351
|
+
"embed",
|
|
352
|
+
"fieldset",
|
|
353
|
+
"figcaption",
|
|
354
|
+
"figure",
|
|
355
|
+
"footer",
|
|
356
|
+
"form",
|
|
357
|
+
"h1",
|
|
358
|
+
"h2",
|
|
359
|
+
"h3",
|
|
360
|
+
"h4",
|
|
361
|
+
"h5",
|
|
362
|
+
"h6",
|
|
363
|
+
"head",
|
|
364
|
+
"header",
|
|
365
|
+
"hr",
|
|
366
|
+
"html",
|
|
367
|
+
"i",
|
|
368
|
+
"iframe",
|
|
369
|
+
"img",
|
|
370
|
+
"input",
|
|
371
|
+
"ins",
|
|
372
|
+
"kbd",
|
|
373
|
+
"label",
|
|
374
|
+
"legend",
|
|
375
|
+
"li",
|
|
376
|
+
"link",
|
|
377
|
+
"main",
|
|
378
|
+
"map",
|
|
379
|
+
"mark",
|
|
380
|
+
"meta",
|
|
381
|
+
"meter",
|
|
382
|
+
"nav",
|
|
383
|
+
"noscript",
|
|
384
|
+
"object",
|
|
385
|
+
"ol",
|
|
386
|
+
"optgroup",
|
|
387
|
+
"option",
|
|
388
|
+
"output",
|
|
389
|
+
"p",
|
|
390
|
+
"param",
|
|
391
|
+
"picture",
|
|
392
|
+
"pre",
|
|
393
|
+
"progress",
|
|
394
|
+
"q",
|
|
395
|
+
"rp",
|
|
396
|
+
"rt",
|
|
397
|
+
"ruby",
|
|
398
|
+
"s",
|
|
399
|
+
"samp",
|
|
400
|
+
"script",
|
|
401
|
+
"section",
|
|
402
|
+
"select",
|
|
403
|
+
"small",
|
|
404
|
+
"source",
|
|
405
|
+
"span",
|
|
406
|
+
"strong",
|
|
407
|
+
"style",
|
|
408
|
+
"sub",
|
|
409
|
+
"summary",
|
|
410
|
+
"sup",
|
|
411
|
+
"svg",
|
|
412
|
+
"table",
|
|
413
|
+
"tbody",
|
|
414
|
+
"td",
|
|
415
|
+
"template",
|
|
416
|
+
"textarea",
|
|
417
|
+
"tfoot",
|
|
418
|
+
"th",
|
|
419
|
+
"thead",
|
|
420
|
+
"time",
|
|
421
|
+
"title",
|
|
422
|
+
"tr",
|
|
423
|
+
"track",
|
|
424
|
+
"u",
|
|
425
|
+
"ul",
|
|
426
|
+
"var",
|
|
427
|
+
"video",
|
|
428
|
+
"wbr",
|
|
418
429
|
].reduce((result, name) => {
|
|
419
430
|
result[name] = node(name)
|
|
420
431
|
return result
|
|
@@ -428,9 +439,9 @@ function classes() {
|
|
|
428
439
|
continue
|
|
429
440
|
}
|
|
430
441
|
const type = typeof arg
|
|
431
|
-
if (type ===
|
|
442
|
+
if (type === "string") {
|
|
432
443
|
array.push(arg)
|
|
433
|
-
} else if (type ===
|
|
444
|
+
} else if (type === "object") {
|
|
434
445
|
for (const key in arg) {
|
|
435
446
|
if (arg[key]) {
|
|
436
447
|
array.push(key)
|
|
@@ -438,7 +449,13 @@ function classes() {
|
|
|
438
449
|
}
|
|
439
450
|
}
|
|
440
451
|
}
|
|
441
|
-
return array.join(
|
|
452
|
+
return array.join(" ")
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function i18n(translations) {
|
|
456
|
+
return function translate(language, key) {
|
|
457
|
+
return translations[key][language]
|
|
458
|
+
}
|
|
442
459
|
}
|
|
443
460
|
|
|
444
461
|
module.exports = {
|
|
@@ -451,5 +468,6 @@ module.exports = {
|
|
|
451
468
|
css,
|
|
452
469
|
js,
|
|
453
470
|
tag,
|
|
471
|
+
i18n,
|
|
454
472
|
...nodes,
|
|
455
473
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "boxwood",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.65.0",
|
|
4
4
|
"description": "Compile HTML templates into JS",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
},
|
|
46
46
|
"homepage": "https://github.com/buxlabs/boxwood#readme",
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"ava": "^5.
|
|
48
|
+
"ava": "^5.2.0",
|
|
49
49
|
"benchmark": "2.1.4",
|
|
50
50
|
"express": "^4.18.2",
|
|
51
51
|
"handlebars": "^4.7.7",
|
|
52
|
-
"jsdom": "^
|
|
52
|
+
"jsdom": "^21.1.0",
|
|
53
53
|
"lodash.template": "4.5.0",
|
|
54
54
|
"mustache": "^4.2.0",
|
|
55
55
|
"nyc": "15.1.0",
|