boxwood 0.64.0 → 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 +225 -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,21 +12,23 @@ async function compile(path) {
|
|
|
12
12
|
const styles = []
|
|
13
13
|
const scripts = []
|
|
14
14
|
const walk = (node) => {
|
|
15
|
-
if (!node) {
|
|
16
|
-
|
|
15
|
+
if (!node) {
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
if (node.name === "head") {
|
|
17
19
|
nodes.head = node
|
|
18
20
|
}
|
|
19
|
-
if (node.name ===
|
|
21
|
+
if (node.name === "body") {
|
|
20
22
|
nodes.body = node
|
|
21
23
|
}
|
|
22
|
-
if (node.name ===
|
|
24
|
+
if (node.name === "style") {
|
|
23
25
|
const css = node.children
|
|
24
26
|
if (!styles.includes(css)) {
|
|
25
27
|
styles.push(css)
|
|
26
28
|
}
|
|
27
29
|
node.ignore = true
|
|
28
30
|
}
|
|
29
|
-
if (node.name ===
|
|
31
|
+
if (node.name === "script") {
|
|
30
32
|
const js = node.children
|
|
31
33
|
if (!scripts.includes(js)) {
|
|
32
34
|
scripts.push(js)
|
|
@@ -43,16 +45,16 @@ async function compile(path) {
|
|
|
43
45
|
if (nodes.head) {
|
|
44
46
|
if (styles.length > 0) {
|
|
45
47
|
nodes.head.children.push({
|
|
46
|
-
name:
|
|
47
|
-
children: styles.join(
|
|
48
|
+
name: "style",
|
|
49
|
+
children: styles.join(""),
|
|
48
50
|
})
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
if (nodes.body) {
|
|
52
54
|
if (scripts.length > 0) {
|
|
53
55
|
nodes.body.children.push({
|
|
54
|
-
name:
|
|
55
|
-
children: scripts.join(
|
|
56
|
+
name: "script",
|
|
57
|
+
children: scripts.join(""),
|
|
56
58
|
})
|
|
57
59
|
}
|
|
58
60
|
}
|
|
@@ -62,11 +64,11 @@ async function compile(path) {
|
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
const ENTITIES = {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"'":
|
|
69
|
-
'"':
|
|
67
|
+
"&": "&",
|
|
68
|
+
"<": "<",
|
|
69
|
+
">": ">",
|
|
70
|
+
"'": "'",
|
|
71
|
+
'"': """,
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
const REGEXP = /[&<>'"]/g
|
|
@@ -78,47 +80,47 @@ const escapeHTML = (string) => {
|
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
const BOOLEAN_ATTRIBUTES = [
|
|
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
|
-
|
|
116
|
-
|
|
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",
|
|
117
119
|
]
|
|
118
120
|
|
|
119
121
|
const ALIASES = {
|
|
120
|
-
className:
|
|
121
|
-
htmlFor:
|
|
122
|
+
className: "class",
|
|
123
|
+
htmlFor: "for",
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
const attributes = (options) => {
|
|
@@ -132,59 +134,59 @@ const attributes = (options) => {
|
|
|
132
134
|
const name = ALIASES[key] || key
|
|
133
135
|
const value = options[key]
|
|
134
136
|
const content = Array.isArray(value) ? classes(...value) : value
|
|
135
|
-
result.push(name +
|
|
137
|
+
result.push(name + "=" + '"' + content + '"')
|
|
136
138
|
}
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
|
-
return result.join(
|
|
141
|
+
return result.join(" ")
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
const SELF_CLOSING_TAGS = [
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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",
|
|
160
162
|
]
|
|
161
163
|
|
|
162
164
|
const isUnescapedTag = (name) => {
|
|
163
|
-
return ![
|
|
165
|
+
return !["script", "style", "template"].includes(name)
|
|
164
166
|
}
|
|
165
167
|
|
|
166
168
|
const render = (input, escape = true) => {
|
|
167
169
|
if (input.ignore) {
|
|
168
|
-
return
|
|
170
|
+
return ""
|
|
169
171
|
}
|
|
170
172
|
if (Array.isArray(input)) {
|
|
171
|
-
return input.filter(Boolean).map(render).join(
|
|
173
|
+
return input.filter(Boolean).map(render).join("")
|
|
172
174
|
}
|
|
173
|
-
if (typeof input ===
|
|
175
|
+
if (typeof input === "number") {
|
|
174
176
|
return input.toString()
|
|
175
177
|
}
|
|
176
|
-
if (typeof input ===
|
|
178
|
+
if (typeof input === "string") {
|
|
177
179
|
return escape ? escapeHTML(input) : input
|
|
178
180
|
}
|
|
179
|
-
if (input.name ===
|
|
181
|
+
if (input.name === "fragment") {
|
|
180
182
|
return render(input.children)
|
|
181
183
|
}
|
|
182
|
-
if (input.name ===
|
|
184
|
+
if (input.name === "raw") {
|
|
183
185
|
return render(input.children, false)
|
|
184
186
|
}
|
|
185
187
|
if (SELF_CLOSING_TAGS.includes(input.name)) {
|
|
186
188
|
if (input.attributes) {
|
|
187
|
-
return `<${input.name} ` + attributes(input.attributes) +
|
|
189
|
+
return `<${input.name} ` + attributes(input.attributes) + ">"
|
|
188
190
|
}
|
|
189
191
|
return `<${input.name}>`
|
|
190
192
|
}
|
|
@@ -192,7 +194,7 @@ const render = (input, escape = true) => {
|
|
|
192
194
|
return (
|
|
193
195
|
`<${input.name} ` +
|
|
194
196
|
attributes(input.attributes) +
|
|
195
|
-
|
|
197
|
+
">" +
|
|
196
198
|
render(input.children, isUnescapedTag(input.name)) +
|
|
197
199
|
`</${input.name}>`
|
|
198
200
|
)
|
|
@@ -203,22 +205,26 @@ const render = (input, escape = true) => {
|
|
|
203
205
|
)
|
|
204
206
|
}
|
|
205
207
|
if (input.children) {
|
|
206
|
-
return
|
|
208
|
+
return (
|
|
209
|
+
`<${input.name}>` +
|
|
210
|
+
render(input.children, isUnescapedTag(input.name)) +
|
|
211
|
+
`</${input.name}>`
|
|
212
|
+
)
|
|
207
213
|
}
|
|
208
214
|
return `<${input.name}></${input.name}>`
|
|
209
215
|
}
|
|
210
216
|
|
|
211
217
|
const fragment = (children) => {
|
|
212
|
-
return { name:
|
|
218
|
+
return { name: "fragment", children }
|
|
213
219
|
}
|
|
214
220
|
|
|
215
221
|
const raw = (children) => {
|
|
216
|
-
return { name:
|
|
222
|
+
return { name: "raw", children }
|
|
217
223
|
}
|
|
218
224
|
|
|
219
225
|
raw.load = function () {
|
|
220
226
|
const path = join(...arguments)
|
|
221
|
-
const content = readFileSync(path,
|
|
227
|
+
const content = readFileSync(path, "utf8")
|
|
222
228
|
return raw(content)
|
|
223
229
|
}
|
|
224
230
|
|
|
@@ -248,7 +254,7 @@ const tag = (a, b, c) => {
|
|
|
248
254
|
}
|
|
249
255
|
|
|
250
256
|
function css(inputs) {
|
|
251
|
-
let result =
|
|
257
|
+
let result = ""
|
|
252
258
|
for (let i = 0, ilen = inputs.length; i < ilen; i += 1) {
|
|
253
259
|
const input = inputs[i]
|
|
254
260
|
const value = arguments[i + 1]
|
|
@@ -263,7 +269,7 @@ function css(inputs) {
|
|
|
263
269
|
const classes = {}
|
|
264
270
|
|
|
265
271
|
csstree.walk(tree, (node) => {
|
|
266
|
-
if (node.type ===
|
|
272
|
+
if (node.type === "ClassSelector") {
|
|
267
273
|
const name = `__${node.name}__${hash}`
|
|
268
274
|
classes[node.name] = name
|
|
269
275
|
node.name = name
|
|
@@ -272,18 +278,20 @@ function css(inputs) {
|
|
|
272
278
|
|
|
273
279
|
return {
|
|
274
280
|
...classes,
|
|
275
|
-
css: tag(
|
|
281
|
+
css: tag("style", csstree.generate(tree)),
|
|
276
282
|
}
|
|
277
283
|
}
|
|
278
284
|
|
|
279
285
|
css.load = function () {
|
|
280
286
|
const path = join(...arguments)
|
|
281
|
-
const content = readFileSync(path,
|
|
282
|
-
return css
|
|
287
|
+
const content = readFileSync(path, "utf8")
|
|
288
|
+
return css`
|
|
289
|
+
${content}
|
|
290
|
+
`
|
|
283
291
|
}
|
|
284
292
|
|
|
285
293
|
function js(inputs) {
|
|
286
|
-
let result =
|
|
294
|
+
let result = ""
|
|
287
295
|
for (let i = 0, ilen = inputs.length; i < ilen; i += 1) {
|
|
288
296
|
const input = inputs[i]
|
|
289
297
|
const value = arguments[i + 1]
|
|
@@ -294,130 +302,130 @@ function js(inputs) {
|
|
|
294
302
|
}
|
|
295
303
|
}
|
|
296
304
|
return {
|
|
297
|
-
js: tag(
|
|
305
|
+
js: tag("script", result),
|
|
298
306
|
}
|
|
299
307
|
}
|
|
300
308
|
|
|
301
309
|
js.load = function () {
|
|
302
310
|
const path = join(...arguments)
|
|
303
|
-
const content = readFileSync(path,
|
|
311
|
+
const content = readFileSync(path, "utf8")
|
|
304
312
|
return js`${content}`
|
|
305
313
|
}
|
|
306
314
|
|
|
307
315
|
const node = (name) => (options, children) => tag(name, options, children)
|
|
308
|
-
const doctype = node(
|
|
316
|
+
const doctype = node("!DOCTYPE html")
|
|
309
317
|
|
|
310
318
|
const nodes = [
|
|
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
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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",
|
|
421
429
|
].reduce((result, name) => {
|
|
422
430
|
result[name] = node(name)
|
|
423
431
|
return result
|
|
@@ -431,9 +439,9 @@ function classes() {
|
|
|
431
439
|
continue
|
|
432
440
|
}
|
|
433
441
|
const type = typeof arg
|
|
434
|
-
if (type ===
|
|
442
|
+
if (type === "string") {
|
|
435
443
|
array.push(arg)
|
|
436
|
-
} else if (type ===
|
|
444
|
+
} else if (type === "object") {
|
|
437
445
|
for (const key in arg) {
|
|
438
446
|
if (arg[key]) {
|
|
439
447
|
array.push(key)
|
|
@@ -441,7 +449,13 @@ function classes() {
|
|
|
441
449
|
}
|
|
442
450
|
}
|
|
443
451
|
}
|
|
444
|
-
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
|
+
}
|
|
445
459
|
}
|
|
446
460
|
|
|
447
461
|
module.exports = {
|
|
@@ -454,5 +468,6 @@ module.exports = {
|
|
|
454
468
|
css,
|
|
455
469
|
js,
|
|
456
470
|
tag,
|
|
471
|
+
i18n,
|
|
457
472
|
...nodes,
|
|
458
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",
|