@maizzle/framework 4.8.7 → 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 +65 -58
  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 +46 -14
  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 +52 -23
  22. package/src/transformers/comb.js +51 -0
  23. package/src/transformers/core.js +20 -0
  24. package/src/transformers/filters/defaultFilters.js +90 -70
  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 -26
  33. package/src/transformers/removeAttributes.js +17 -17
  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 +15 -15
  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 -56
  69. package/src/transformers/removeInlineSizes.js +0 -43
  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,41 +1,70 @@
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>
@@ -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,31 +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
-
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)
16
+ // biome-ignore lint: not confusing
23
17
  const escape = content => content.replace(/["&'<>]/g, m => escapeMap[m])
18
+
24
19
  const escapeOnce = content => escape(unescape(content))
20
+
25
21
  const floor = content => Math.floor(Number.parseFloat(content))
22
+
26
23
  const lowercase = content => content.toLowerCase()
24
+
27
25
  const lstrip = content => content.replace(/^\s+/, '')
26
+
28
27
  const minus = (content, attribute) => Number.parseFloat(content) - Number.parseFloat(attribute)
28
+
29
29
  const modulo = (content, attribute) => Number.parseFloat(content) % Number.parseFloat(attribute)
30
+
30
31
  const multiply = (content, attribute) => Number.parseFloat(content) * Number.parseFloat(attribute)
32
+
31
33
  const newlineToBr = content => content.replace(/\n/g, '<br>')
34
+
32
35
  const plus = (content, attribute) => Number.parseFloat(content) + Number.parseFloat(attribute)
36
+
33
37
  const prepend = (content, attribute) => attribute + content
34
38
 
35
39
  const remove = (content, attribute) => {
@@ -38,6 +42,7 @@ const remove = (content, attribute) => {
38
42
  }
39
43
 
40
44
  const removeFirst = (content, attribute) => content.replace(attribute, '')
45
+
41
46
  const replace = (content, attribute) => {
42
47
  const [search, replace] = attribute.split('|')
43
48
  const regex = new RegExp(search, 'g')
@@ -50,77 +55,92 @@ const replaceFirst = (content, attribute) => {
50
55
  }
51
56
 
52
57
  const round = content => Math.round(Number.parseFloat(content))
58
+
53
59
  const rstrip = content => content.replace(/\s+$/, '')
60
+
54
61
  const uppercase = content => content.toUpperCase()
62
+
55
63
  const size = content => content.length
64
+
56
65
  const slice = (content, attribute) => {
57
- try {
58
- const [start, end] = attribute.split(',')
59
- return content.slice(start, end)
60
- } catch {
66
+ const [start, end] = attribute.split(',')
67
+
68
+ if (!end && !start) {
69
+ return content
70
+ }
71
+
72
+ if (!end) {
61
73
  return content.slice(attribute)
62
74
  }
75
+
76
+ return content.slice(start, end)
63
77
  }
64
78
 
65
79
  const stripNewlines = content => content.replace(/\n/g, '')
80
+
66
81
  const trim = content => content.trim()
82
+
67
83
  const truncate = (content, attribute) => {
68
- try {
69
- const [length, omission] = attribute.split(',')
70
- return content.length > Number.parseInt(length, 10) ?
71
- content.slice(0, length) + (omission || '...') :
72
- content
73
- } catch {
74
- const length = Number.parseInt(attribute, 10)
75
- return content.length > length ? content.slice(0, length) + '...' : content
76
- }
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
77
89
  }
78
90
 
79
91
  const truncateWords = (content, attribute) => {
80
- try {
81
- const [length, omission] = attribute.split(',')
82
- return content.split(' ').slice(0, Number.parseInt(length, 10)).join(' ') + (omission || '...')
83
- } catch {
84
- const length = Number.parseInt(attribute, 10)
85
- return content.split(' ').slice(0, length).join(' ') + '...'
86
- }
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;': '\''
87
105
  }
106
+ // biome-ignore lint: not confusing
107
+ const unescape = string => string.replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m])
88
108
 
89
- // eslint-disable-next-line
90
109
  const urlDecode = content => content.split('+').map(decodeURIComponent).join(' ')
91
- // eslint-disable-next-line
110
+
92
111
  const urlEncode = content => content.split(' ').map(encodeURIComponent).join('+')
93
112
 
94
- exports.append = append
95
- exports.capitalize = capitalize
96
- exports.ceil = ceil
97
- exports['divide-by'] = divide
98
- exports.divide = divide
99
- exports.escape = escape
100
- exports['escape-once'] = escapeOnce
101
- exports.floor = floor
102
- exports.lowercase = lowercase
103
- exports.lstrip = lstrip
104
- exports.minus = minus
105
- exports.modulo = modulo
106
- exports.multiply = multiply
107
- exports['newline-to-br'] = newlineToBr
108
- exports.plus = plus
109
- exports.prepend = prepend
110
- exports.remove = remove
111
- exports['remove-first'] = removeFirst
112
- exports.replace = replace
113
- exports['replace-first'] = replaceFirst
114
- exports.round = round
115
- exports.rstrip = rstrip
116
- exports.uppercase = uppercase
117
- exports.size = size
118
- exports.slice = slice
119
- exports.strip = trim
120
- exports['strip-newlines'] = stripNewlines
121
- exports.times = multiply
122
- exports.trim = trim
123
- exports.truncate = truncate
124
- exports['truncate-words'] = truncateWords
125
- exports['url-decode'] = urlDecode
126
- 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
+ }
@@ -1,83 +1,19 @@
1
- const posthtml = require('posthtml')
2
- const {get, merge, omit, has} = require('lodash')
3
- const defaultFilters = require('./defaultFilters')
4
- const PostCSS = require('../../generators/postcss')
5
- const posthtmlContent = require('posthtml-content')
6
- const Tailwind = require('../../generators/tailwindcss')
7
- const safeClassNames = require('posthtml-safe-class-names')
8
- const defaultConfig = require('../../generators/posthtml/defaultConfig')
1
+ import posthtml from 'posthtml'
2
+ import { defu as merge } from 'defu'
3
+ import posthtmlContent from 'posthtml-content'
4
+ import posthtmlConfig from '../../posthtml/defaultConfig.js'
5
+ import { filters as defaultFilters } from './defaultFilters.js'
9
6
 
10
- module.exports = async (html, config = {}, direct = false) => {
11
- if (get(config, 'filters') === false) {
12
- return html
13
- }
7
+ export default function posthtmlPlugin(filters = {}) {
8
+ filters = merge(defaultFilters, filters)
14
9
 
15
- const filters = direct ?
16
- merge(defaultFilters, config) :
17
- merge(defaultFilters, get(config, 'filters', {}))
18
-
19
- const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
20
-
21
- /**
22
- * Compile CSS in <style {post|tailwind}css> tags
23
- */
24
- const maizzleConfig = omit(config, ['build.tailwind.css', 'css'])
25
-
26
- filters.postcss = css => PostCSS.process(css, maizzleConfig)
27
- filters.tailwindcss = css => Tailwind.compile({
28
- css,
29
- html,
30
- config: merge({
31
- build: {
32
- tailwind: {
33
- config: get(config, 'build.tailwind.config', 'tailwind.config.js')
34
- }
35
- }
36
- }, maizzleConfig)
37
- })
38
-
39
- const posthtmlPlugins = [
40
- styleDataEmbed(),
41
- posthtmlContent(filters)
42
- ]
43
-
44
- /**
45
- * Run `safeClassNames` in filters only when not when developing locally and
46
- * `safeClassNames` is not explicitly disabled (set to `false`).
47
- */
48
- if (get(config, 'env') !== 'local' && get(config, 'safeClassNames') !== false) {
49
- posthtmlPlugins.push(safeClassNames({
50
- replacements: {
51
- '{': '{',
52
- '}': '}'
53
- }
54
- }))
55
- }
56
-
57
- return posthtml(posthtmlPlugins)
58
- .process(html, posthtmlOptions)
59
- .then(result => result.html)
10
+ return posthtmlContent(filters)
60
11
  }
61
12
 
62
- /**
63
- * Prevent CSS inlining
64
- *
65
- * Add a `data-embed` attribute to <style> tags that we want to preserve.
66
- * Can be used for HTML email client targeting hacks.
67
- */
68
- const styleDataEmbed = () => tree => {
69
- const process = node => {
70
- if (
71
- node.tag === 'style'
72
- && node.attrs
73
- && (has(node.attrs, 'preserve') || has(node.attrs, 'embed'))) {
74
- node.attrs = {...node.attrs, 'data-embed': true}
75
- node.attrs.preserve = false
76
- node.attrs.embed = false
77
- }
78
-
79
- return node
80
- }
81
-
82
- return tree.walk(process)
13
+ export async function filters(html = '', filters = {}, posthtmlOptions = {}) {
14
+ return posthtml([
15
+ posthtmlPlugin(filters)
16
+ ])
17
+ .process(html, merge(posthtmlOptions, posthtmlConfig))
18
+ .then(result => result.html)
83
19
  }