waibu-mpa 2.1.0 → 2.1.1

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 CHANGED
@@ -1,5 +1,7 @@
1
1
  import { stripHtml } from 'string-strip-html'
2
2
  import iconsetMappings from './lib/iconset-mappings.js'
3
+ import toolsFactory from './lib/class/tools.js'
4
+ import widgetFactory from './lib/class/widget.js'
3
5
  import path from 'path'
4
6
 
5
7
  // taken from: https://stackoverflow.com/questions/52928550/js-get-list-of-all-available-standard-html-tags
@@ -130,6 +132,8 @@ async function factory (pkgName) {
130
132
  init = async () => {
131
133
  const { trim } = this.app.lib._
132
134
  this.config.waibu.prefix = trim(this.config.waibu.prefix, '/')
135
+ await toolsFactory.call(this)
136
+ await widgetFactory.call(this)
133
137
  }
134
138
 
135
139
  arrayToAttr = (array = [], delimiter = ' ') => {
@@ -11,7 +11,7 @@ async function injectElements (options) {
11
11
  if (req.darkMode) $('body').attr('data-bs-theme', 'dark')
12
12
  const rsc = {}
13
13
  for (const tag of reply.ctags ?? []) {
14
- let Builder = get(cmp, `factory.${tag}`)
14
+ let Builder = get(cmp, `widget.${tag}`)
15
15
  if (!isFunction(Builder)) continue
16
16
  if (!isClass(Builder)) Builder = await Builder.call(cmp)
17
17
  for (const key of ['scripts', 'css']) {
@@ -1,316 +1,318 @@
1
- import baseFactory from './base-factory.js'
2
1
  import path from 'path'
3
2
 
4
- class Component {
5
- constructor ({ plugin, $, theme, iconset, locals, reply, req, scriptBlock, styleBlock } = {}) {
6
- const { get } = plugin.app.lib._
7
- this.baseFactory = baseFactory
8
- this.plugin = plugin
9
- this.app = plugin.app
10
- this.$ = $
11
- this.theme = theme
12
- this.iconset = iconset
13
- this.locals = locals
14
- this.reply = reply
15
- this.req = req
16
- this.cacheMaxAge = get(plugin, 'app.waibuMpa.config.theme.component.cacheMaxAgeDur', 0)
17
- this.namespace = 'c:'
18
- this.noTags = []
19
- this.scriptBlock = scriptBlock
20
- this.styleBlock = styleBlock
21
- this.factory = {}
22
- }
3
+ async function componentFactory () {
4
+ class MpaComponent extends this.app.baseClass.MpaTools {
5
+ constructor ({ plugin, $, theme, iconset, locals, reply, req, scriptBlock, styleBlock } = {}) {
6
+ super(plugin)
7
+ const { get } = plugin.app.lib._
8
+ this.$ = $
9
+ this.theme = theme
10
+ this.iconset = iconset
11
+ this.locals = locals
12
+ this.reply = reply
13
+ this.req = req
14
+ this.cacheMaxAge = get(plugin, 'app.waibuMpa.config.theme.component.cacheMaxAgeDur', 0)
15
+ this.namespace = 'c:'
16
+ this.noTags = []
17
+ this.scriptBlock = scriptBlock
18
+ this.styleBlock = styleBlock
19
+ this.widget = {}
20
+ }
23
21
 
24
- addScriptBlock = (type, block) => {
25
- if (!block) {
26
- block = type
27
- type = 'root'
22
+ addScriptBlock = (type, block) => {
23
+ if (!block) {
24
+ block = type
25
+ type = 'root'
26
+ }
27
+ if (typeof block === 'string') block = [block]
28
+ this.scriptBlock[type] = this.scriptBlock[type] ?? []
29
+ this.scriptBlock[type].push(...block)
28
30
  }
29
- if (typeof block === 'string') block = [block]
30
- this.scriptBlock[type] = this.scriptBlock[type] ?? []
31
- this.scriptBlock[type].push(...block)
32
- }
33
31
 
34
- addStyleBlock = (type, block) => {
35
- if (!block) {
36
- block = type
37
- type = 'root'
32
+ addStyleBlock = (type, block) => {
33
+ if (!block) {
34
+ block = type
35
+ type = 'root'
36
+ }
37
+ if (typeof block === 'string') block = [block]
38
+ this.styleBlock[type] = this.styleBlock[type] ?? []
39
+ this.styleBlock[type].push(...block)
38
40
  }
39
- if (typeof block === 'string') block = [block]
40
- this.styleBlock[type] = this.styleBlock[type] ?? []
41
- this.styleBlock[type].push(...block)
42
- }
43
41
 
44
- /**
45
- * Loads base factories dynamically from the file system.
46
- * @async
47
- */
42
+ /**
43
+ * Loads base factories dynamically from the file system.
44
+ * @async
45
+ */
48
46
 
49
- loadBaseFactories = async () => {
50
- const { camelCase } = this.app.lib._
51
- const { importModule } = this.app.bajo
52
- const { fastGlob } = this.app.lib
53
- const pattern = `${this.app.waibuMpa.dir.pkg}/lib/class/factory/*.js`
54
- const files = await fastGlob(pattern)
55
- for (const file of files) {
56
- const mod = await importModule(file)
57
- const name = camelCase(path.basename(file, '.js'))
58
- this.factory[name] = mod
47
+ loadBaseWidgets = async () => {
48
+ const { camelCase } = this.app.lib._
49
+ const { importModule } = this.app.bajo
50
+ const { fastGlob } = this.app.lib
51
+ const pattern = `${this.app.waibuMpa.dir.pkg}/lib/class/widget/*.js`
52
+ const files = await fastGlob(pattern)
53
+ for (const file of files) {
54
+ const mod = await importModule(file)
55
+ const name = camelCase(path.basename(file, '.js'))
56
+ this.widget[name] = await mod.call(this.plugin)
57
+ }
59
58
  }
60
- }
61
59
 
62
- /**
63
- * Retrieves a builder class or instance for a specific method.
64
- * @async
65
- * @param {string} method - The method name to retrieve the builder for.
66
- * @returns {Promise<Function|Object>} The builder class or instance.
67
- */
68
- getBuilder = async (method) => {
69
- const { isClass } = this.app.lib.aneka
70
- let Builder = this.factory[method]
71
- if (!isClass(Builder)) Builder = await Builder.call(this)
72
- return Builder
73
- }
74
-
75
- /**
76
- * Determines the method to use based on the provided parameters.
77
- * @param {Object} params - The parameters containing the method information.
78
- * @returns {string} The determined method name.
79
- */
80
- getMethod = (params) => {
81
- let method = params.ctag
82
- if (!this.factory[method]) method = 'any'
83
- return method
84
- }
60
+ /**
61
+ * Retrieves a widget builder class or instance for a specific method.
62
+ * @async
63
+ * @param {string} method - The method name to retrieve the builder for.
64
+ * @returns {Promise<Function|Object>} The builder class or instance.
65
+ */
66
+ getWidget = async (method) => {
67
+ const { isClass } = this.app.lib.aneka
68
+ let Widget = this.widget[method]
69
+ if (!isClass(Widget)) Widget = await Widget.call(this.plugin)
70
+ return Widget
71
+ }
85
72
 
86
- /**
87
- * Builds a tag with the given parameters and options.
88
- * @async
89
- * @param {Object} [params={}] - The parameters for the tag.
90
- * @param {Object} [opts={}] - Additional options for building the tag.
91
- * @returns {Promise<string|boolean>} The rendered HTML or `false` if the build fails.
92
- */
93
- buildTag = async (params = {}, opts = {}) => {
94
- const { sprintf } = this.app.lib
95
- const { compile } = this.app.bajoTemplate
96
- const { escape } = this.app.waibu
97
- const { isEmpty, merge, uniq, without } = this.app.lib._
98
- params.ctag = params.tag
99
- const method = this.getMethod(params)
100
- if (opts.attr) params.attr = merge({}, opts.attr, params.attr)
101
- this.normalizeAttr(params)
102
- params.attr.content = params.attr.content ?? ''
103
- let html
104
- if (params.attr.content.includes('%s')) html = sprintf(params.attr.content, params.html)
105
- else if (isEmpty(params.html)) html = params.attr.content
106
- if (!isEmpty(html)) params.html = params.noEscape ? html : escape(html)
107
- await this.iconAttr(params, method)
108
- await this.beforeBuildTag(method, params)
109
- const Builder = await this.getBuilder(method)
110
- const builder = new Builder({ component: this, params })
111
- const resp = await builder.build()
112
- if (resp === false) {
113
- return false
73
+ /**
74
+ * Determines the method to use based on the provided parameters.
75
+ * @param {Object} params - The parameters containing the method information.
76
+ * @returns {string} The determined method name.
77
+ */
78
+ getMethod = (params) => {
79
+ let method = params.ctag
80
+ if (!this.widget[method]) method = 'any'
81
+ return method
114
82
  }
115
83
 
116
- await this.afterBuildTag(method, params)
117
- params.attr.class = without(uniq(params.attr.class), undefined, null, '')
118
- if (isEmpty(params.attr.class)) delete params.attr.class
119
- if (isEmpty(params.attr.style)) delete params.attr.style
120
- if (isEmpty(params.html)) return await this.render(params)
84
+ /**
85
+ * Builds a tag with the given parameters and options.
86
+ * @async
87
+ * @param {Object} [params={}] - The parameters for the tag.
88
+ * @param {Object} [opts={}] - Additional options for building the tag.
89
+ * @returns {Promise<string|boolean>} The rendered HTML or `false` if the build fails.
90
+ */
91
+ buildTag = async (params = {}, opts = {}) => {
92
+ const { sprintf } = this.app.lib
93
+ const { compile } = this.app.bajoTemplate
94
+ const { escape } = this.app.waibu
95
+ const { isEmpty, merge, uniq, without } = this.app.lib._
96
+ params.ctag = params.tag
97
+ const method = this.getMethod(params)
98
+ if (opts.attr) params.attr = merge({}, opts.attr, params.attr)
99
+ this.normalizeAttr(params)
100
+ params.attr.content = params.attr.content ?? ''
101
+ let html
102
+ if (params.attr.content.includes('%s')) html = sprintf(params.attr.content, params.html)
103
+ else if (isEmpty(params.html)) html = params.attr.content
104
+ if (!isEmpty(html)) params.html = params.noEscape ? html : escape(html)
105
+ await this.iconAttr(params, method)
106
+ await this.beforeBuildTag(method, params)
107
+ const Widget = await this.getWidget(method)
108
+ const widget = new Widget({ component: this, params })
109
+ const resp = await widget.build()
110
+ if (resp === false) {
111
+ return false
112
+ }
121
113
 
122
- const merged = merge({}, params.locals, { attr: params.attr })
123
- const result = await compile(params.html, merged, { ttl: this.cacheMaxAge })
124
- params.html = result
125
- return await this.render(params)
126
- }
114
+ await this.afterBuildTag(method, params)
115
+ params.attr.class = without(uniq(params.attr.class), undefined, null, '')
116
+ if (isEmpty(params.attr.class)) delete params.attr.class
117
+ if (isEmpty(params.attr.style)) delete params.attr.style
118
+ if (isEmpty(params.html)) return await this.render(params)
127
119
 
128
- /**
129
- * Normalizes the attributes of the given parameters.
130
- * @param {Object} [params={}] - The parameters containing attributes to normalize.
131
- * @param {Object} [opts={}] - Additional options for normalization.
132
- */
133
- normalizeAttr = (params = {}, opts = {}) => {
134
- const { without, keys, isString, isArray } = this.app.lib._
135
- const { generateId } = this.app.lib.aneka
136
- params.attr = params.attr ?? {}
137
- params.attr.class = this.app.waibuMpa.attrToArray(params.attr.class)
138
- params.attr.style = this.app.waibuMpa.attrToObject(params.attr.style)
139
- if (isString(opts.cls)) params.attr.class.push(opts.cls)
140
- else if (isArray(opts.cls)) params.attr.class.push(...opts.cls)
141
- if (opts.tag) params.tag = opts.tag
142
- if (opts.autoId) params.attr.id = isString(params.attr.id) ? params.attr.id : generateId('alpha')
143
- for (const k of without(keys(opts), 'cls', 'tag', 'autoId')) {
144
- params.attr[k] = opts[k]
120
+ const merged = merge({}, params.locals, { attr: params.attr })
121
+ const result = await compile(params.html, merged, { ttl: this.cacheMaxAge })
122
+ params.html = result
123
+ return await this.render(params)
145
124
  }
146
- params.html = params.html ?? ''
147
- }
148
125
 
149
- /**
150
- * Renders the final HTML for the given parameters.
151
- * @async
152
- * @param {Object} [params={}] - The parameters for rendering.
153
- * @returns {Promise<string>} The rendered HTML.
154
- */
155
- render = async (params = {}) => {
156
- const { omit, isEmpty, merge, kebabCase, isArray } = this.app.lib._
157
- const { stringifyAttribs } = this.app.waibuMpa
158
- params.attr = params.attr ?? {}
126
+ /**
127
+ * Normalizes the attributes of the given parameters.
128
+ * @param {Object} [params={}] - The parameters containing attributes to normalize.
129
+ * @param {Object} [opts={}] - Additional options for normalization.
130
+ */
131
+ normalizeAttr = (params = {}, opts = {}) => {
132
+ const { without, keys, isString, isArray } = this.app.lib._
133
+ const { generateId } = this.app.lib.aneka
134
+ params.attr = params.attr ?? {}
135
+ params.attr.class = this.app.waibuMpa.attrToArray(params.attr.class)
136
+ params.attr.style = this.app.waibuMpa.attrToObject(params.attr.style)
137
+ if (isString(opts.cls)) params.attr.class.push(opts.cls)
138
+ else if (isArray(opts.cls)) params.attr.class.push(...opts.cls)
139
+ if (opts.tag) params.tag = opts.tag
140
+ if (opts.autoId) params.attr.id = isString(params.attr.id) ? params.attr.id : generateId('alpha')
141
+ for (const k of without(keys(opts), 'cls', 'tag', 'autoId')) {
142
+ params.attr[k] = opts[k]
143
+ }
144
+ params.html = params.html ?? ''
145
+ }
159
146
 
160
- params.tag = params.attr.tag ?? ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(params.tag) ? params.tag : kebabCase(params.tag)
161
- params.attr = omit(params.attr, ['tag', 'content'])
162
- params.html = params.html ?? ''
163
- params.attrs = stringifyAttribs(params.attr)
164
- if (!isEmpty(params.attrs)) params.attrs = ' ' + params.attrs
165
- let html = isArray(params.html) ? params.html.join('\n') : params.html
166
- if (!params.noTag) {
167
- html = params.selfClosing ? `<${params.tag}${params.attrs}/>` : `<${params.tag}${params.attrs}>${params.html}</${params.tag}>`
168
- const method = this.getMethod(params)
169
- if (this.factory[method]) { // static method on Builder
170
- const Builder = await this.getBuilder(method)
171
- if (Builder.after) {
172
- html = (await Builder.after.call(this, merge({}, params, { result: html }))) ?? html
147
+ /**
148
+ * Renders the final HTML for the given parameters.
149
+ * @async
150
+ * @param {Object} [params={}] - The parameters for rendering.
151
+ * @returns {Promise<string>} The rendered HTML.
152
+ */
153
+ render = async (params = {}) => {
154
+ const { omit, isEmpty, merge, kebabCase, isArray } = this.app.lib._
155
+ const { stringifyAttribs } = this.app.waibuMpa
156
+ params.attr = params.attr ?? {}
157
+
158
+ params.tag = params.attr.tag ?? ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(params.tag) ? params.tag : kebabCase(params.tag)
159
+ params.attr = omit(params.attr, ['tag', 'content'])
160
+ params.html = params.html ?? ''
161
+ params.attrs = stringifyAttribs(params.attr)
162
+ if (!isEmpty(params.attrs)) params.attrs = ' ' + params.attrs
163
+ let html = isArray(params.html) ? params.html.join('\n') : params.html
164
+ if (!params.noTag) {
165
+ html = params.selfClosing ? `<${params.tag}${params.attrs}/>` : `<${params.tag}${params.attrs}>${params.html}</${params.tag}>`
166
+ const method = this.getMethod(params)
167
+ if (this.widget[method]) { // static method on Widget
168
+ const Widget = await this.getWidget(method)
169
+ if (Widget.after) {
170
+ html = (await Widget.after.call(this, merge({}, params, { result: html }))) ?? html
171
+ }
173
172
  }
174
- }
175
- } else if (!this.noTags.includes(params.attr.octag)) this.noTags.push(params.attr.octag)
176
- if (params.prepend) html = `${params.prepend}${html}`
177
- if (params.append) html += params.append
178
- return html
179
- }
173
+ } else if (!this.noTags.includes(params.attr.octag)) this.noTags.push(params.attr.octag)
174
+ if (params.prepend) html = `${params.prepend}${html}`
175
+ if (params.append) html += params.append
176
+ return html
177
+ }
180
178
 
181
- /**
182
- * Processes icon-related attributes and appends them to the HTML.
183
- * @async
184
- * @param {Object} [params={}] - The parameters containing icon attributes.
185
- * @param {string} method - The method name.
186
- */
187
- iconAttr = async (params = {}, method) => {
188
- if (['modal', 'toast'].includes(method)) return
189
- const { groupAttrs } = this.app.waibuMpa
190
- const { merge } = this.app.lib._
191
- for (const k in params.attr) {
192
- const v = params.attr[k]
193
- if (!['icon', 'iconEnd'].includes(k)) continue
194
- const group = groupAttrs(params.attr, ['icon', 'iconEnd'])
195
- const _params = { attr: merge(k.endsWith('End') ? group.iconEnd : group.icon, { name: v }) }
196
- const Builder = this.factory.icon
197
- const builder = new Builder({ component: this, params: _params })
198
- await builder.build()
199
- const icon = await this.render(_params)
200
- params.html = k.endsWith('End') ? `${params.html} ${icon}` : `${icon} ${params.html}`
201
- delete params.attr[k]
179
+ /**
180
+ * Processes icon-related attributes and appends them to the HTML.
181
+ * @async
182
+ * @param {Object} [params={}] - The parameters containing icon attributes.
183
+ * @param {string} method - The method name.
184
+ */
185
+ iconAttr = async (params = {}, method) => {
186
+ if (['modal', 'toast'].includes(method)) return
187
+ const { groupAttrs } = this.app.waibuMpa
188
+ const { merge } = this.app.lib._
189
+ for (const k in params.attr) {
190
+ const v = params.attr[k]
191
+ if (!['icon', 'iconEnd'].includes(k)) continue
192
+ const group = groupAttrs(params.attr, ['icon', 'iconEnd'])
193
+ const _params = { attr: merge(k.endsWith('End') ? group.iconEnd : group.icon, { name: v }) }
194
+ const Widget = this.widget.icon
195
+ const widget = new Widget({ component: this, params: _params })
196
+ await widget.build()
197
+ const icon = await this.render(_params)
198
+ params.html = k.endsWith('End') ? `${params.html} ${icon}` : `${icon} ${params.html}`
199
+ delete params.attr[k]
200
+ }
202
201
  }
203
- }
204
202
 
205
- /**
206
- * Builds the `<option>` elements for a `<select>` tag.
207
- * @param {Object} params - The parameters containing options and values.
208
- * @returns {string} The generated `<option>` elements.
209
- */
210
- buildOptions = (params) => {
211
- const { has, omit, find, isPlainObject, isArray } = this.app.lib._
212
- const { attrToArray } = this.app.waibuMpa
213
- const items = []
214
- if (!has(params.attr, 'options')) return
215
- let values = attrToArray(params.attr.value)
216
- if (!has(params.attr, 'multiple') && values.length > 0) values = [values[0]]
217
- const options = isArray(params.attr.options) ? params.attr.options : attrToArray(params.attr.options)
218
- for (const opt of options) {
219
- let val
220
- let text
221
- if (isPlainObject(opt)) {
222
- val = opt.value
223
- text = opt.text
224
- } else [val, text] = opt.split('|')
225
- const sel = find(values, v => val === v)
226
- items.push(`<option value="${val}"${sel ? ' selected' : ''}>${this.req.t(text ?? val)}</option>`)
203
+ /**
204
+ * Builds the `<option>` elements for a `<select>` tag.
205
+ * @param {Object} params - The parameters containing options and values.
206
+ * @returns {string} The generated `<option>` elements.
207
+ */
208
+ buildOptions = (params) => {
209
+ const { has, omit, find, isPlainObject, isArray } = this.app.lib._
210
+ const { attrToArray } = this.app.waibuMpa
211
+ const items = []
212
+ if (!has(params.attr, 'options')) return
213
+ let values = attrToArray(params.attr.value)
214
+ if (!has(params.attr, 'multiple') && values.length > 0) values = [values[0]]
215
+ const options = isArray(params.attr.options) ? params.attr.options : attrToArray(params.attr.options)
216
+ for (const opt of options) {
217
+ let val
218
+ let text
219
+ if (isPlainObject(opt)) {
220
+ val = opt.value
221
+ text = opt.text
222
+ } else [val, text] = opt.split('|')
223
+ const sel = find(values, v => val === v)
224
+ items.push(`<option value="${val}"${sel ? ' selected' : ''}>${this.req.t(text ?? val)}</option>`)
225
+ }
226
+ params.attr = omit(params.attr, ['options'])
227
+ return items.join('\n')
227
228
  }
228
- params.attr = omit(params.attr, ['options'])
229
- return items.join('\n')
230
- }
231
229
 
232
- /**
233
- * Builds a URL with the given parameters.
234
- * @param {Object} options - The options for building the URL.
235
- * @param {Array<string>} [options.exclude] - Keys to exclude from the URL.
236
- * @param {string} [options.prefix] - The prefix for the URL.
237
- * @param {string} [options.base] - The base URL.
238
- * @param {string} [options.url] - The URL to modify.
239
- * @param {Object} [options.params={}] - Query parameters to include.
240
- * @param {boolean} [options.prettyUrl] - Whether to generate a pretty URL.
241
- * @returns {string} The built URL.
242
- */
243
- buildUrl = ({ exclude, prefix, base, url, params = {}, prettyUrl }) => {
244
- const { isEmpty } = this.app.lib._
245
- const { buildUrl } = this.app.waibuMpa
246
- url = url ?? (isEmpty(this.req.referer) ? this.req.url : this.req.referer)
247
- return buildUrl({ exclude, prefix, base, url, params, prettyUrl })
248
- }
230
+ /**
231
+ * Builds a URL with the given parameters.
232
+ * @param {Object} options - The options for building the URL.
233
+ * @param {Array<string>} [options.exclude] - Keys to exclude from the URL.
234
+ * @param {string} [options.prefix] - The prefix for the URL.
235
+ * @param {string} [options.base] - The base URL.
236
+ * @param {string} [options.url] - The URL to modify.
237
+ * @param {Object} [options.params={}] - Query parameters to include.
238
+ * @param {boolean} [options.prettyUrl] - Whether to generate a pretty URL.
239
+ * @returns {string} The built URL.
240
+ */
241
+ buildUrl = ({ exclude, prefix, base, url, params = {}, prettyUrl }) => {
242
+ const { isEmpty } = this.app.lib._
243
+ const { buildUrl } = this.app.waibuMpa
244
+ url = url ?? (isEmpty(this.req.referer) ? this.req.url : this.req.referer)
245
+ return buildUrl({ exclude, prefix, base, url, params, prettyUrl })
246
+ }
249
247
 
250
- /**
251
- * Builds a sentence with optional rendering and minification.
252
- * @async
253
- * @param {string|string[]} sentence - The sentence or array of sentences to build.
254
- * @param {Object} [params={}] - Parameters for rendering the sentence.
255
- * @param {Object} [extra={}] - Extra options such as wrapping or minification.
256
- * @returns {Promise<string>} The built sentence.
257
- */
258
- buildSentence = async (sentence, params = {}, extra = {}) => {
259
- if (Array.isArray(sentence)) sentence = sentence.join(' ')
260
- const { minify, renderString } = this.app.waibuMpa
261
- if (extra.wrapped) sentence = '<w>' + sentence + '</w>'
262
- const opts = {
263
- partial: true,
264
- ext: params.ext ?? '.html',
265
- req: this.req,
266
- reply: this.reply
248
+ /**
249
+ * Builds a sentence with optional rendering and minification.
250
+ * @async
251
+ * @param {string|string[]} sentence - The sentence or array of sentences to build.
252
+ * @param {Object} [params={}] - Parameters for rendering the sentence.
253
+ * @param {Object} [extra={}] - Extra options such as wrapping or minification.
254
+ * @returns {Promise<string>} The built sentence.
255
+ */
256
+ buildSentence = async (sentence, params = {}, extra = {}) => {
257
+ if (Array.isArray(sentence)) sentence = sentence.join(' ')
258
+ const { minify, renderString } = this.app.waibuMpa
259
+ if (extra.wrapped) sentence = '<w>' + sentence + '</w>'
260
+ const opts = {
261
+ partial: true,
262
+ ext: params.ext ?? '.html',
263
+ req: this.req,
264
+ reply: this.reply
265
+ }
266
+ let html = await renderString(sentence, params, opts)
267
+ if (extra.wrapped) html = html.slice(3, html.length - 4)
268
+ if (extra.minify) html = await minify(html)
269
+ return html
267
270
  }
268
- let html = await renderString(sentence, params, opts)
269
- if (extra.wrapped) html = html.slice(3, html.length - 4)
270
- if (extra.minify) html = await minify(html)
271
- return html
272
- }
273
271
 
274
- /**
275
- * Hook executed before building a tag.
276
- * @param {string} tag - The tag name.
277
- * @param {Object} params - The parameters for the tag.
278
- */
279
- beforeBuildTag = (tag, params) => {}
272
+ /**
273
+ * Hook executed before building a tag.
274
+ * @param {string} tag - The tag name.
275
+ * @param {Object} params - The parameters for the tag.
276
+ */
277
+ beforeBuildTag = (tag, params) => {}
280
278
 
281
- /**
282
- * Hook executed after building a tag.
283
- * @param {string} tag - The tag name.
284
- * @param {Object} params - The parameters for the tag.
285
- */
286
- afterBuildTag = (tag, params) => {}
279
+ /**
280
+ * Hook executed after building a tag.
281
+ * @param {string} tag - The tag name.
282
+ * @param {Object} params - The parameters for the tag.
283
+ */
284
+ afterBuildTag = (tag, params) => {}
287
285
 
288
- /**
289
- * Builds a child tag based on a detector attribute.
290
- * @async
291
- * @param {string} detector - The attribute to detect child tags.
292
- * @param {Object} options - Options for building the child tag.
293
- * @param {string} [options.tag] - The tag name.
294
- * @param {Object} options.params - The parameters for the child tag.
295
- * @param {string} [options.inner] - Inner HTML for the child tag.
296
- */
297
- buildChildTag = async (detector, { tag, params, inner }) => {
298
- const { has, pickBy, omit, keys } = this.app.lib._
299
- if (has(params.attr, detector)) {
300
- const [prefix] = detector.split('-')
301
- const attr = {}
302
- const html = tag ? params.attr[detector] : undefined
303
- tag = tag ?? prefix
304
- const picked = pickBy(params.attr, (v, k) => k.startsWith(`${prefix}-`))
305
- for (const k in picked) {
306
- attr[k.slice(prefix.length + 1)] = picked[k]
286
+ /**
287
+ * Builds a child tag based on a detector attribute.
288
+ * @async
289
+ * @param {string} detector - The attribute to detect child tags.
290
+ * @param {Object} options - Options for building the child tag.
291
+ * @param {string} [options.tag] - The tag name.
292
+ * @param {Object} options.params - The parameters for the child tag.
293
+ * @param {string} [options.inner] - Inner HTML for the child tag.
294
+ */
295
+ buildChildTag = async (detector, { tag, params, inner }) => {
296
+ const { has, pickBy, omit, keys } = this.app.lib._
297
+ if (has(params.attr, detector)) {
298
+ const [prefix] = detector.split('-')
299
+ const attr = {}
300
+ const html = tag ? params.attr[detector] : undefined
301
+ tag = tag ?? prefix
302
+ const picked = pickBy(params.attr, (v, k) => k.startsWith(`${prefix}-`))
303
+ for (const k in picked) {
304
+ attr[k.slice(prefix.length + 1)] = picked[k]
305
+ }
306
+ const child = await this.buildTag({ tag, params: { attr, html } })
307
+ params.html += `\n${child}`
308
+ const excluded = [detector, ...keys(picked)]
309
+ params.attr = omit(params.attr, excluded)
307
310
  }
308
- const child = await this.buildTag({ tag, params: { attr, html } })
309
- params.html += `\n${child}`
310
- const excluded = [detector, ...keys(picked)]
311
- params.attr = omit(params.attr, excluded)
312
311
  }
313
312
  }
313
+
314
+ this.app.baseClass.MpaComponent = MpaComponent
315
+ return MpaComponent
314
316
  }
315
317
 
316
- export default Component
318
+ export default componentFactory