@maizzle/framework 4.8.8 → 5.0.0-beta.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.
Files changed (80) hide show
  1. package/bin/maizzle +3 -1
  2. package/package.json +64 -55
  3. package/src/commands/build.js +244 -19
  4. package/src/commands/serve.js +2 -197
  5. package/src/generators/plaintext.js +192 -91
  6. package/src/generators/render.js +128 -0
  7. package/src/index.js +45 -13
  8. package/src/{generators/posthtml → posthtml}/defaultComponentsConfig.js +6 -4
  9. package/src/{generators/posthtml → posthtml}/defaultConfig.js +1 -1
  10. package/src/posthtml/index.js +74 -0
  11. package/src/posthtml/plugins/expandLinkTag.js +36 -0
  12. package/src/server/client.js +181 -0
  13. package/src/server/index.js +383 -0
  14. package/src/server/routes/hmr.js +24 -0
  15. package/src/server/routes/index.js +38 -0
  16. package/src/server/views/error.html +83 -0
  17. package/src/server/views/index.html +24 -0
  18. package/src/server/websockets.js +27 -0
  19. package/src/transformers/addAttributes.js +30 -0
  20. package/src/transformers/attributeToStyle.js +30 -36
  21. package/src/transformers/baseUrl.js +56 -27
  22. package/src/transformers/comb.js +51 -0
  23. package/src/transformers/core.js +20 -0
  24. package/src/transformers/filters/defaultFilters.js +90 -71
  25. package/src/transformers/filters/index.js +14 -78
  26. package/src/transformers/index.js +268 -63
  27. package/src/transformers/inline.js +240 -0
  28. package/src/transformers/markdown.js +13 -14
  29. package/src/transformers/minify.js +21 -16
  30. package/src/transformers/posthtmlMso.js +13 -8
  31. package/src/transformers/prettify.js +16 -15
  32. package/src/transformers/preventWidows.js +32 -28
  33. package/src/transformers/removeAttributes.js +19 -19
  34. package/src/transformers/replaceStrings.js +30 -9
  35. package/src/transformers/safeClassNames.js +24 -24
  36. package/src/transformers/shorthandCss.js +22 -0
  37. package/src/transformers/sixHex.js +17 -17
  38. package/src/transformers/urlParameters.js +18 -16
  39. package/src/transformers/useAttributeSizes.js +65 -0
  40. package/src/utils/getConfigByFilePath.js +124 -0
  41. package/src/utils/node.js +68 -0
  42. package/src/utils/string.js +117 -0
  43. package/types/build.d.ts +117 -57
  44. package/types/components.d.ts +130 -112
  45. package/types/config.d.ts +454 -242
  46. package/types/css/inline.d.ts +234 -0
  47. package/types/css/purge.d.ts +125 -0
  48. package/types/events.d.ts +5 -105
  49. package/types/index.d.ts +148 -116
  50. package/types/markdown.d.ts +20 -18
  51. package/types/minify.d.ts +122 -120
  52. package/types/plaintext.d.ts +46 -52
  53. package/types/posthtml.d.ts +103 -136
  54. package/types/render.d.ts +0 -117
  55. package/types/urlParameters.d.ts +21 -20
  56. package/types/widowWords.d.ts +9 -7
  57. package/src/functions/plaintext.js +0 -5
  58. package/src/functions/render.js +0 -5
  59. package/src/generators/config.js +0 -52
  60. package/src/generators/output/index.js +0 -4
  61. package/src/generators/output/to-disk.js +0 -254
  62. package/src/generators/output/to-string.js +0 -73
  63. package/src/generators/postcss.js +0 -23
  64. package/src/generators/posthtml/index.js +0 -75
  65. package/src/generators/tailwindcss.js +0 -157
  66. package/src/transformers/extraAttributes.js +0 -33
  67. package/src/transformers/inlineCss.js +0 -42
  68. package/src/transformers/removeInlineBackgroundColor.js +0 -57
  69. package/src/transformers/removeInlineSizes.js +0 -45
  70. package/src/transformers/removeInlinedSelectors.js +0 -100
  71. package/src/transformers/removeUnusedCss.js +0 -48
  72. package/src/transformers/shorthandInlineCSS.js +0 -26
  73. package/src/utils/helpers.js +0 -13
  74. package/types/baseUrl.d.ts +0 -79
  75. package/types/fetch.d.ts +0 -143
  76. package/types/inlineCss.d.ts +0 -207
  77. package/types/layouts.d.ts +0 -39
  78. package/types/removeUnusedCss.d.ts +0 -115
  79. package/types/tailwind.d.ts +0 -22
  80. package/types/templates.d.ts +0 -181
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Maizzle | Templates</title>
7
+ </head>
8
+ <body>
9
+ <each loop="items, index in templates">
10
+ <div>
11
+ <h2>{{ index }}</h2>
12
+ <ul>
13
+ <each loop="item in items">
14
+ <li>
15
+ <a href="{{ item.href }}">
16
+ {{ item.base }}
17
+ </a>
18
+ </li>
19
+ </each>
20
+ </ul>
21
+ </div>
22
+ </each>
23
+ </body>
24
+ </html>
@@ -0,0 +1,27 @@
1
+ import WebSocket from 'ws'
2
+
3
+ export function initWebSockets(wss, options = {}) {
4
+ options.shouldScroll = options.shouldScroll || false
5
+ options.useHmr = options.useHmr || true
6
+
7
+ wss.on('connection', ws => {
8
+ // Handle incoming messages from the client
9
+ ws.on('message', message => {
10
+ const parsedMessage = JSON.parse(message)
11
+
12
+ /**
13
+ * Broadcast message back to all connected clients
14
+ * We use it to send the scroll position back so other clients can follow
15
+ */
16
+ wss.clients.forEach(client => {
17
+ if (client.readyState === WebSocket.OPEN) {
18
+ client.send(JSON.stringify({
19
+ ...parsedMessage,
20
+ scrollSync: options.shouldScroll,
21
+ hmr: options.useHmr
22
+ }))
23
+ }
24
+ })
25
+ })
26
+ })
27
+ }
@@ -0,0 +1,30 @@
1
+ import posthtml from 'posthtml'
2
+ import { defu as merge } from 'defu'
3
+ import posthtmlConfig from '../posthtml/defaultConfig.js'
4
+ import addAttributesPlugin from 'posthtml-extra-attributes'
5
+
6
+ export default function posthtmlPlugin(attributes = {}) {
7
+ const defaultAttributes = {
8
+ table: {
9
+ cellpadding: 0,
10
+ cellspacing: 0,
11
+ role: 'none'
12
+ },
13
+ img: {
14
+ alt: ''
15
+ }
16
+ }
17
+
18
+ // User-defined attributes take precedence
19
+ attributes = merge(attributes, defaultAttributes)
20
+
21
+ return addAttributesPlugin({ attributes })
22
+ }
23
+
24
+ export async function addAttributes(html = '', attributes = {}, posthtmlOptions = {}) {
25
+ return posthtml([
26
+ posthtmlPlugin(attributes)
27
+ ])
28
+ .process(html, merge(posthtmlOptions, posthtmlConfig))
29
+ .then(result => result.html)
30
+ }
@@ -1,44 +1,28 @@
1
- const {
2
- get,
3
- merge,
4
- forEach,
5
- intersection,
6
- keys,
7
- isEmpty
8
- } = require('lodash')
9
- const posthtml = require('posthtml')
10
- const parseAttrs = require('posthtml-attrs-parser')
11
- const defaultConfig = require('../generators/posthtml/defaultConfig')
12
-
13
- module.exports = async (html, config = {}, direct = false) => {
14
- const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
15
- const attributes = get(config, 'inlineCSS.attributeToStyle', false)
16
-
17
- if (typeof attributes === 'boolean' && attributes) {
18
- return posthtml([attributesToStyle()]).process(html, posthtmlOptions).then(result => result.html)
1
+ import posthtml from 'posthtml'
2
+ import get from 'lodash-es/get.js'
3
+ import { defu as merge } from 'defu'
4
+ import keys from 'lodash-es/keys.js'
5
+ import forEach from 'lodash-es/forEach.js'
6
+ import parseAttrs from 'posthtml-attrs-parser'
7
+ import intersection from 'lodash-es/intersection.js'
8
+ import posthtmlConfig from '../posthtml/defaultConfig.js'
9
+
10
+ const posthtmlPlugin = (attributes = []) => tree => {
11
+ if (!Array.isArray(attributes)) {
12
+ return tree
19
13
  }
20
14
 
21
- if (Array.isArray(attributes) && !isEmpty(attributes)) {
22
- return posthtml([attributesToStyle({attributes})]).process(html, posthtmlOptions).then(result => result.html)
15
+ if (attributes.length === 0) {
16
+ return tree
23
17
  }
24
18
 
25
- if (direct) {
26
- return posthtml([
27
- attributesToStyle({
28
- attributes: Array.isArray(config) ? config : []
29
- })
30
- ]).process(html, posthtmlOptions).then(result => result.html)
31
- }
32
-
33
- return html
34
- }
35
-
36
- const attributesToStyle = (options = {}) => tree => {
37
- options.attributes = options.attributes || ['width', 'height', 'bgcolor', 'background', 'align', 'valign']
38
-
39
19
  const process = node => {
20
+ if (!node.attrs) {
21
+ return node
22
+ }
23
+
40
24
  const nodeAttributes = parseAttrs(node.attrs)
41
- const matches = intersection(keys(nodeAttributes), options.attributes)
25
+ const matches = intersection(keys(nodeAttributes), attributes)
42
26
  const nodeStyle = get(node.attrs, 'style')
43
27
  const cssToInline = []
44
28
 
@@ -87,7 +71,7 @@ const attributesToStyle = (options = {}) => tree => {
87
71
  }
88
72
  })
89
73
 
90
- nodeAttributes.style = nodeStyle ? `${nodeStyle} ${cssToInline.join('; ')}` : `${cssToInline.join('; ')}`
74
+ nodeAttributes.style = nodeStyle ? `${nodeStyle.split(';').join(';')} ${cssToInline.join('; ')}` : `${cssToInline.join('; ')}`
91
75
 
92
76
  node.attrs = nodeAttributes.compose()
93
77
 
@@ -96,3 +80,13 @@ const attributesToStyle = (options = {}) => tree => {
96
80
 
97
81
  return tree.walk(process)
98
82
  }
83
+
84
+ export default posthtmlPlugin
85
+
86
+ export async function attributeToStyle(html = '', attributes = [], posthtmlOptions = {}) {
87
+ return posthtml([
88
+ posthtmlPlugin(attributes)
89
+ ])
90
+ .process(html, merge(posthtmlOptions, posthtmlConfig))
91
+ .then(result => result.html)
92
+ }
@@ -1,48 +1,77 @@
1
- const posthtml = require('posthtml')
2
- const isUrl = require('is-url-superb')
3
- const baseUrl = require('posthtml-base-url')
4
- const {get, merge, isObject, isEmpty} = require('lodash')
5
- const defaultConfig = require('../generators/posthtml/defaultConfig')
1
+ import posthtml from 'posthtml'
2
+ import isUrl from 'is-url-superb'
3
+ import get from 'lodash-es/get.js'
4
+ import { defu as merge } from 'defu'
5
+ import baseUrl from 'posthtml-base-url'
6
+ import { render } from 'posthtml-render'
7
+ import isEmpty from 'lodash-es/isEmpty.js'
8
+ import isObject from 'lodash-es/isObject.js'
9
+ import { parser as parse } from 'posthtml-parser'
10
+ import posthtmlConfig from '../posthtml/defaultConfig.js'
6
11
 
7
- module.exports = async (html, config = {}, direct = false) => {
8
- const url = direct ? config : get(config, 'baseURL', get(config, 'baseUrl'))
9
- const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
10
-
11
- // Handle `baseUrl` as a string
12
+ const posthtmlPlugin = url => tree => {
13
+ // Handle `baseURL` as a string
12
14
  if (typeof url === 'string' && url.length > 0) {
13
- html = rewriteVMLs(html, url)
15
+ const html = rewriteVMLs(render(tree), url)
14
16
 
15
- return posthtml([
16
- baseUrl({url, allTags: true, styleTag: true, inlineCss: true})
17
- ])
18
- .process(html, posthtmlOptions)
19
- .then(result => result.html)
17
+ return baseUrl({
18
+ url,
19
+ allTags: true,
20
+ styleTag: true,
21
+ inlineCss: true
22
+ })(parse(html, posthtmlConfig))
20
23
  }
21
24
 
22
25
  // Handle `baseURL` as an object
23
26
  if (isObject(url) && !isEmpty(url)) {
24
- html = rewriteVMLs(html, get(url, 'url', ''))
27
+ const html = rewriteVMLs(render(tree), get(url, 'url', ''))
28
+ const {
29
+ styleTag = true,
30
+ inlineCss = true,
31
+ allTags,
32
+ tags,
33
+ url: baseURL,
34
+ ...posthtmlOptions
35
+ } = url
25
36
 
26
- return posthtml([
27
- baseUrl(merge({styleTag: true, inlineCss: true}, url))
28
- ])
29
- .process(html, posthtmlOptions)
30
- .then(result => result.html)
37
+ return baseUrl({
38
+ styleTag,
39
+ inlineCss,
40
+ allTags,
41
+ tags,
42
+ url: baseURL,
43
+ })(parse(html, merge(posthtmlConfig, posthtmlOptions)))
31
44
  }
32
45
 
33
- return html
46
+ return tree
47
+ }
48
+
49
+ export default posthtmlPlugin
50
+
51
+ export async function addBaseUrl(html = '', options = {}, posthtmlOptions = {}) {
52
+ return posthtml([
53
+ posthtmlPlugin(options)
54
+ ])
55
+ .process(html, merge(posthtmlOptions, posthtmlConfig))
56
+ .then(result => result.html)
34
57
  }
35
58
 
36
59
  /**
60
+ * Handle VML
61
+ *
37
62
  * VML backgrounds must be handled with regex because
38
63
  * they're inside HTML comments.
64
+ *
65
+ * @param {string} html The HTML content
66
+ * @param {string} url The base URL to prepend
67
+ * @returns {string} The modified HTML
39
68
  */
40
69
  const rewriteVMLs = (html, url) => {
41
70
  // Handle <v:image>
42
71
  const vImageMatches = html.match(/<v:image[^>]+src="?([^"\s]+)"/g)
43
72
 
44
73
  if (vImageMatches) {
45
- for (const match of vImageMatches) {
74
+ vImageMatches.forEach(match => {
46
75
  const vImage = match.match(/<v:image[^>]+src="?([^"\s]+)"/)
47
76
  const vImageSrc = vImage[1]
48
77
 
@@ -51,14 +80,14 @@ const rewriteVMLs = (html, url) => {
51
80
  const vImageReplace = vImage[0].replace(vImageSrc, vImageSrcUrl)
52
81
  html = html.replace(vImage[0], vImageReplace)
53
82
  }
54
- }
83
+ })
55
84
  }
56
85
 
57
86
  // Handle <v:fill>
58
87
  const vFillMatches = html.match(/<v:fill[^>]+src="?([^"\s]+)"/g)
59
88
 
60
89
  if (vFillMatches) {
61
- for (const match of vFillMatches) {
90
+ vFillMatches.forEach(match => {
62
91
  const vFill = match.match(/<v:fill[^>]+src="?([^"\s]+)"/)
63
92
  const vFillSrc = vFill[1]
64
93
 
@@ -67,7 +96,7 @@ const rewriteVMLs = (html, url) => {
67
96
  const vFillReplace = vFill[0].replace(vFillSrc, vFillSrcUrl)
68
97
  html = html.replace(vFill[0], vFillReplace)
69
98
  }
70
- }
99
+ })
71
100
  }
72
101
 
73
102
  return html
@@ -0,0 +1,51 @@
1
+ import posthtml from 'posthtml'
2
+ import get from 'lodash-es/get.js'
3
+ import { defu as merge } from 'defu'
4
+ import { render } from 'posthtml-render'
5
+ import { comb as emailComb } from 'email-comb'
6
+ import { parser as parse } from 'posthtml-parser'
7
+ import posthtmlConfig from '../posthtml/defaultConfig.js'
8
+
9
+ const posthtmlPlugin = options => tree => {
10
+ const defaultSafelist = [
11
+ '*body*', // Gmail
12
+ '.gmail*', // Gmail
13
+ '.apple*', // Apple Mail
14
+ '.ios*', // Mail on iOS
15
+ '.ox-*', // Open-Xchange
16
+ '.outlook*', // Outlook.com
17
+ '[data-ogs*', // Outlook.com
18
+ '.bloop_container', // Airmail
19
+ '.Singleton', // Apple Mail 10
20
+ '.unused', // Notes 8
21
+ '.moz-text-html', // Thunderbird
22
+ '.mail-detail-content', // Comcast, Libero webmail
23
+ '*edo*', // Edison (all)
24
+ '#*', // Freenet uses #msgBody
25
+ '.lang*' // Fenced code blocks
26
+ ]
27
+
28
+ const defaultOptions = {
29
+ backend: [
30
+ { heads: '{{', tails: '}}' },
31
+ { heads: '{%', tails: '%}' },
32
+ ],
33
+ whitelist: [...defaultSafelist, ...get(options, 'whitelist', [])]
34
+ }
35
+
36
+ options = merge(options, defaultOptions)
37
+
38
+ const { result: html } = emailComb(render(tree), options)
39
+
40
+ return parse(html)
41
+ }
42
+
43
+ export default posthtmlPlugin
44
+
45
+ export async function comb(html = '', options = {}, posthtmlOptions = {}) {
46
+ return posthtml([
47
+ posthtmlPlugin(options)
48
+ ])
49
+ .process(html, merge(posthtmlOptions, posthtmlConfig))
50
+ .then(result => result.html)
51
+ }
@@ -0,0 +1,20 @@
1
+ const posthtmlPlugin = (config = {}) => tree => {
2
+ const process = node => {
3
+ /**
4
+ * Remove plaintext tags when developing locally
5
+ */
6
+ if (
7
+ config._dev
8
+ && node.tag === 'plaintext'
9
+ ) {
10
+ node.tag = false
11
+ node.content = ['']
12
+ }
13
+
14
+ return node
15
+ }
16
+
17
+ return tree.walk(process)
18
+ }
19
+
20
+ export default posthtmlPlugin
@@ -1,3 +1,11 @@
1
+ const append = (content, attribute) => content + attribute
2
+
3
+ const capitalize = content => content.charAt(0).toUpperCase() + content.slice(1)
4
+
5
+ const ceil = content => Math.ceil(Number.parseFloat(content))
6
+
7
+ const divide = (content, attribute) => Number.parseFloat(content) / Number.parseFloat(attribute)
8
+
1
9
  const escapeMap = {
2
10
  '&': '&amp;',
3
11
  '<': '&lt;',
@@ -5,32 +13,27 @@ const escapeMap = {
5
13
  '"': '&#34;',
6
14
  '\'': '&#39;'
7
15
  }
8
-
9
- const unescapeMap = {
10
- '&amp;': '&',
11
- '&lt;': '<',
12
- '&gt;': '>',
13
- '&#34;': '"',
14
- '&#39;': '\''
15
- }
16
- // biome-ignore lint: not shadowing
17
- const unescape = string => string.replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m])
18
-
19
- const append = (content, attribute) => content + attribute
20
- const capitalize = content => content.charAt(0).toUpperCase() + content.slice(1)
21
- const ceil = content => Math.ceil(Number.parseFloat(content))
22
- const divide = (content, attribute) => Number.parseFloat(content) / Number.parseFloat(attribute)
23
- // biome-ignore lint: not shadowing
16
+ // biome-ignore lint: not confusing
24
17
  const escape = content => content.replace(/["&'<>]/g, m => escapeMap[m])
18
+
25
19
  const escapeOnce = content => escape(unescape(content))
20
+
26
21
  const floor = content => Math.floor(Number.parseFloat(content))
22
+
27
23
  const lowercase = content => content.toLowerCase()
24
+
28
25
  const lstrip = content => content.replace(/^\s+/, '')
26
+
29
27
  const minus = (content, attribute) => Number.parseFloat(content) - Number.parseFloat(attribute)
28
+
30
29
  const modulo = (content, attribute) => Number.parseFloat(content) % Number.parseFloat(attribute)
30
+
31
31
  const multiply = (content, attribute) => Number.parseFloat(content) * Number.parseFloat(attribute)
32
+
32
33
  const newlineToBr = content => content.replace(/\n/g, '<br>')
34
+
33
35
  const plus = (content, attribute) => Number.parseFloat(content) + Number.parseFloat(attribute)
36
+
34
37
  const prepend = (content, attribute) => attribute + content
35
38
 
36
39
  const remove = (content, attribute) => {
@@ -39,6 +42,7 @@ const remove = (content, attribute) => {
39
42
  }
40
43
 
41
44
  const removeFirst = (content, attribute) => content.replace(attribute, '')
45
+
42
46
  const replace = (content, attribute) => {
43
47
  const [search, replace] = attribute.split('|')
44
48
  const regex = new RegExp(search, 'g')
@@ -51,77 +55,92 @@ const replaceFirst = (content, attribute) => {
51
55
  }
52
56
 
53
57
  const round = content => Math.round(Number.parseFloat(content))
58
+
54
59
  const rstrip = content => content.replace(/\s+$/, '')
60
+
55
61
  const uppercase = content => content.toUpperCase()
62
+
56
63
  const size = content => content.length
64
+
57
65
  const slice = (content, attribute) => {
58
- try {
59
- const [start, end] = attribute.split(',')
60
- return content.slice(start, end)
61
- } catch {
66
+ const [start, end] = attribute.split(',')
67
+
68
+ if (!end && !start) {
69
+ return content
70
+ }
71
+
72
+ if (!end) {
62
73
  return content.slice(attribute)
63
74
  }
75
+
76
+ return content.slice(start, end)
64
77
  }
65
78
 
66
79
  const stripNewlines = content => content.replace(/\n/g, '')
80
+
67
81
  const trim = content => content.trim()
82
+
68
83
  const truncate = (content, attribute) => {
69
- try {
70
- const [length, omission] = attribute.split(',')
71
- return content.length > Number.parseInt(length, 10) ?
72
- content.slice(0, length) + (omission || '...') :
73
- content
74
- } catch {
75
- const length = Number.parseInt(attribute, 10)
76
- return content.length > length ? content.slice(0, length) + '...' : content
77
- }
84
+ const [length, omission] = attribute.split(',')
85
+
86
+ return content && content.length > Number.parseInt(length, 10)
87
+ ? content.slice(0, length) + (omission || '...')
88
+ : content // content is shorter than length required to truncate
78
89
  }
79
90
 
80
91
  const truncateWords = (content, attribute) => {
81
- try {
82
- const [length, omission] = attribute.split(',')
83
- return content.split(' ').slice(0, Number.parseInt(length, 10)).join(' ') + (omission || '...')
84
- } catch {
85
- const length = Number.parseInt(attribute, 10)
86
- return content.split(' ').slice(0, length).join(' ') + '...'
87
- }
92
+ const [length, omission] = attribute.split(',')
93
+
94
+ return content.split(' ')
95
+ .slice(0, Number.parseInt(length, 10))
96
+ .join(' ') + (omission || '...')
97
+ }
98
+
99
+ const unescapeMap = {
100
+ '&amp;': '&',
101
+ '&lt;': '<',
102
+ '&gt;': '>',
103
+ '&#34;': '"',
104
+ '&#39;': '\''
88
105
  }
106
+ // biome-ignore lint: not confusing
107
+ const unescape = string => string.replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m])
89
108
 
90
- // eslint-disable-next-line
91
109
  const urlDecode = content => content.split('+').map(decodeURIComponent).join(' ')
92
- // eslint-disable-next-line
110
+
93
111
  const urlEncode = content => content.split(' ').map(encodeURIComponent).join('+')
94
112
 
95
- exports.append = append
96
- exports.capitalize = capitalize
97
- exports.ceil = ceil
98
- exports['divide-by'] = divide
99
- exports.divide = divide
100
- exports.escape = escape
101
- exports['escape-once'] = escapeOnce
102
- exports.floor = floor
103
- exports.lowercase = lowercase
104
- exports.lstrip = lstrip
105
- exports.minus = minus
106
- exports.modulo = modulo
107
- exports.multiply = multiply
108
- exports['newline-to-br'] = newlineToBr
109
- exports.plus = plus
110
- exports.prepend = prepend
111
- exports.remove = remove
112
- exports['remove-first'] = removeFirst
113
- exports.replace = replace
114
- exports['replace-first'] = replaceFirst
115
- exports.round = round
116
- exports.rstrip = rstrip
117
- exports.uppercase = uppercase
118
- exports.size = size
119
- exports.slice = slice
120
- exports.strip = trim
121
- exports['strip-newlines'] = stripNewlines
122
- exports.times = multiply
123
- exports.trim = trim
124
- exports.truncate = truncate
125
- exports['truncate-words'] = truncateWords
126
- exports['url-decode'] = urlDecode
127
- exports['url-encode'] = urlEncode
113
+ export const filters = {
114
+ append,
115
+ capitalize,
116
+ ceil,
117
+ 'divide-by': divide,
118
+ escape,
119
+ 'escape-once': escapeOnce,
120
+ floor,
121
+ lowercase,
122
+ lstrip,
123
+ minus,
124
+ modulo,
125
+ multiply,
126
+ 'newline-to-br': newlineToBr,
127
+ plus,
128
+ prepend,
129
+ remove,
130
+ 'remove-first': removeFirst,
131
+ replace,
132
+ 'replace-first': replaceFirst,
133
+ round,
134
+ rstrip,
135
+ uppercase,
136
+ size,
137
+ slice,
138
+ 'strip-newlines': stripNewlines,
139
+ times: multiply,
140
+ trim,
141
+ truncate,
142
+ 'truncate-words': truncateWords,
143
+ 'url-decode': urlDecode,
144
+ 'url-encode': urlEncode,
145
+ unescape
146
+ }