boxwood 2.8.2 → 2.10.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/adapters/express/index.js +13 -5
- package/package.json +3 -3
- package/ui/markdown/index.js +66 -31
|
@@ -32,15 +32,23 @@ function engine(options = {}) {
|
|
|
32
32
|
try {
|
|
33
33
|
const template = await compileFile(path)
|
|
34
34
|
// Automatically inject nonce from res.locals if available
|
|
35
|
-
const templateOptions =
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
const templateOptions =
|
|
36
|
+
options && options._locals && options._locals.nonce
|
|
37
|
+
? { ...options, nonce: options._locals.nonce }
|
|
38
|
+
: options
|
|
38
39
|
const html = template(templateOptions)
|
|
39
40
|
if (callback) return callback(null, html)
|
|
40
41
|
return html
|
|
41
42
|
} catch (error) {
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
// Enhance error message with file path for better debugging
|
|
44
|
+
const exception = new Error(
|
|
45
|
+
`Error rendering view "${path}": ${error.message}`,
|
|
46
|
+
)
|
|
47
|
+
exception.stack = error.stack
|
|
48
|
+
exception.originalError = error
|
|
49
|
+
exception.viewPath = path
|
|
50
|
+
if (callback) return callback(exception)
|
|
51
|
+
return exception.message
|
|
44
52
|
}
|
|
45
53
|
}
|
|
46
54
|
return render
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "boxwood",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"description": "Compile HTML templates into JS",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"benchmark": "2.1.4",
|
|
50
50
|
"c8": "^11.0.0",
|
|
51
51
|
"express": "^5.2.1",
|
|
52
|
-
"handlebars": "^4.7.
|
|
53
|
-
"jsdom": "^
|
|
52
|
+
"handlebars": "^4.7.9",
|
|
53
|
+
"jsdom": "^29.1.1",
|
|
54
54
|
"mustache": "^4.2.0",
|
|
55
55
|
"underscore": "^1.13.8"
|
|
56
56
|
},
|
package/ui/markdown/index.js
CHANGED
|
@@ -103,61 +103,96 @@ function Markdown(params, children) {
|
|
|
103
103
|
const lines = children
|
|
104
104
|
.trim()
|
|
105
105
|
.split("\n")
|
|
106
|
-
.
|
|
107
|
-
.
|
|
106
|
+
.filter((line) => line.trim().length > 0)
|
|
107
|
+
.map((line) => {
|
|
108
|
+
// Preserve leading spaces for indentation tracking
|
|
109
|
+
const trimmed = line.trim()
|
|
110
|
+
const leadingSpaces = line.length - line.trimStart().length
|
|
111
|
+
return { text: trimmed, indent: leadingSpaces }
|
|
112
|
+
})
|
|
108
113
|
|
|
109
114
|
const items = lines
|
|
110
|
-
.map((
|
|
111
|
-
if (UNORDERED_MARKERS.some((marker) =>
|
|
112
|
-
const content =
|
|
115
|
+
.map(({ text, indent }) => {
|
|
116
|
+
if (UNORDERED_MARKERS.some((marker) => text.startsWith(marker))) {
|
|
117
|
+
const content = text.substring(2)
|
|
113
118
|
if (!content) return null
|
|
114
|
-
return { type: "li", list: "ul", content }
|
|
119
|
+
return { type: "li", list: "ul", content, indent }
|
|
115
120
|
}
|
|
116
121
|
|
|
117
|
-
if (ORDERED_LIST_REGEXP.test(
|
|
118
|
-
const content =
|
|
122
|
+
if (ORDERED_LIST_REGEXP.test(text)) {
|
|
123
|
+
const content = text.replace(ORDERED_LIST_REGEXP, "")
|
|
119
124
|
if (!content) return null
|
|
120
|
-
return { type: "li", list: "ol", content }
|
|
125
|
+
return { type: "li", list: "ol", content, indent }
|
|
121
126
|
}
|
|
122
127
|
|
|
123
128
|
for (const { prefix, type } of HEADINGS) {
|
|
124
|
-
if (
|
|
125
|
-
return { type, content:
|
|
129
|
+
if (text.startsWith(prefix)) {
|
|
130
|
+
return { type, content: text.substring(prefix.length), indent }
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
133
|
|
|
129
|
-
if (
|
|
130
|
-
return { type: "blockquote", content:
|
|
134
|
+
if (text.startsWith("> ")) {
|
|
135
|
+
return { type: "blockquote", content: text.substring(2), indent }
|
|
131
136
|
}
|
|
132
137
|
|
|
133
|
-
return { type: "p", content:
|
|
138
|
+
return { type: "p", content: text, indent }
|
|
134
139
|
})
|
|
135
140
|
.filter(Boolean)
|
|
136
141
|
|
|
137
142
|
const nodes = []
|
|
138
143
|
let i = 0
|
|
139
144
|
|
|
140
|
-
|
|
141
|
-
const
|
|
145
|
+
function parseList(startIndex, parentIndent) {
|
|
146
|
+
const list = []
|
|
147
|
+
let currentIndex = startIndex
|
|
148
|
+
const parentListType = items[startIndex].list
|
|
142
149
|
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
items[i].type === "li" &&
|
|
150
|
-
items[i].list === parent
|
|
151
|
-
) {
|
|
152
|
-
list.push(Li(params, format(items[i].content)))
|
|
153
|
-
i++
|
|
150
|
+
while (currentIndex < items.length) {
|
|
151
|
+
const item = items[currentIndex]
|
|
152
|
+
|
|
153
|
+
// Stop if not a list item or indent is less than expected
|
|
154
|
+
if (item.type !== "li" || item.indent < parentIndent) {
|
|
155
|
+
break
|
|
154
156
|
}
|
|
155
157
|
|
|
156
|
-
if
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
// Stop if indent matches but list type differs
|
|
159
|
+
if (item.indent === parentIndent && item.list !== parentListType) {
|
|
160
|
+
break
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Skip items with greater indent (they belong to nested lists)
|
|
164
|
+
if (item.indent > parentIndent) {
|
|
165
|
+
break
|
|
160
166
|
}
|
|
167
|
+
|
|
168
|
+
// Process current item at the correct indent level
|
|
169
|
+
const content = [format(item.content)]
|
|
170
|
+
currentIndex++
|
|
171
|
+
|
|
172
|
+
// Check if next item is a nested list
|
|
173
|
+
const nextItem = items[currentIndex]
|
|
174
|
+
if (nextItem?.type === "li" && nextItem.indent > item.indent) {
|
|
175
|
+
const nestedResult = parseList(currentIndex, nextItem.indent)
|
|
176
|
+
content.push(nestedResult.list)
|
|
177
|
+
currentIndex = nestedResult.nextIndex
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
list.push(Li(params, content))
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const listElement =
|
|
184
|
+
parentListType === "ul" ? Ul(params, list) : Ol(params, list)
|
|
185
|
+
|
|
186
|
+
return { list: listElement, nextIndex: currentIndex }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
while (i < items.length) {
|
|
190
|
+
const item = items[i]
|
|
191
|
+
|
|
192
|
+
if (item.type === "li") {
|
|
193
|
+
const result = parseList(i, item.indent)
|
|
194
|
+
nodes.push(result.list)
|
|
195
|
+
i = result.nextIndex
|
|
161
196
|
} else if (item.type === "blockquote") {
|
|
162
197
|
const lines = []
|
|
163
198
|
|