@maizzle/framework 4.2.1 → 4.2.3

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 (40) hide show
  1. package/package.json +2 -3
  2. package/src/generators/plaintext.js +40 -6
  3. package/src/generators/posthtml/defaultConfig.js +3 -0
  4. package/src/generators/{posthtml.js → posthtml/index.js} +2 -1
  5. package/src/generators/tailwindcss.js +9 -2
  6. package/src/transformers/attributeToStyle.js +3 -2
  7. package/src/transformers/baseUrl.js +27 -26
  8. package/src/transformers/extraAttributes.js +3 -2
  9. package/src/transformers/filters/index.js +16 -5
  10. package/src/transformers/index.js +2 -2
  11. package/src/transformers/markdown.js +2 -1
  12. package/src/transformers/posthtmlMso.js +3 -2
  13. package/src/transformers/preventWidows.js +75 -7
  14. package/src/transformers/removeAttributes.js +48 -9
  15. package/src/transformers/removeInlineBackgroundColor.js +8 -4
  16. package/src/transformers/removeInlineSizes.js +8 -5
  17. package/src/transformers/removeInlinedSelectors.js +19 -5
  18. package/src/transformers/removeUnusedCss.js +12 -12
  19. package/src/transformers/replaceStrings.js +1 -1
  20. package/src/transformers/safeClassNames.js +7 -3
  21. package/src/transformers/shorthandInlineCSS.js +3 -2
  22. package/src/transformers/sixHex.js +13 -14
  23. package/src/transformers/urlParameters.js +3 -2
  24. package/src/utils/helpers.js +2 -1
  25. package/test/expected/transformers/base-url.html +1 -1
  26. package/test/fixtures/posthtml/component.html +2 -2
  27. package/test/stubs/breaking/bad.html +2 -2
  28. package/test/stubs/components/component.html +2 -2
  29. package/test/stubs/events/before-create.html +1 -1
  30. package/test/stubs/plaintext/plaintext.html +1 -1
  31. package/test/stubs/templates/1.html +1 -1
  32. package/test/stubs/templates/2.html +1 -0
  33. package/test/stubs/templates/2.test +1 -1
  34. package/test/test-posthtml.js +20 -31
  35. package/test/test-tailwindcss.js +6 -6
  36. package/test/test-todisk.js +14 -64
  37. package/test/test-tostring.js +20 -15
  38. package/test/test-transformers.js +113 -21
  39. package/test/expected/posthtml/fetch.html +0 -5
  40. package/test/fixtures/posthtml/fetch.html +0 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maizzle/framework",
3
- "version": "4.2.1",
3
+ "version": "4.2.3",
4
4
  "description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.",
5
5
  "license": "MIT",
6
6
  "main": "src/index.js",
@@ -65,12 +65,11 @@
65
65
  "posthtml-modules": "^0.9.0",
66
66
  "posthtml-mso": "^1.0.4",
67
67
  "posthtml-postcss-merge-longhand": "^1.0.2",
68
- "posthtml-remove-attributes": "^1.0.0",
69
68
  "posthtml-safe-class-names": "^1.0.8",
70
69
  "posthtml-url-parameters": "^1.0.4",
71
70
  "pretty": "^2.0.0",
72
- "prevent-widows": "^1.0.2",
73
71
  "query-string": "^7.1.0",
72
+ "string-remove-widows": "^2.1.0",
74
73
  "string-strip-html": "^8.2.0",
75
74
  "tailwindcss": "^3.1.0"
76
75
  },
@@ -1,12 +1,44 @@
1
1
  const path = require('path')
2
- const {get} = require('lodash')
3
2
  const posthtml = require('posthtml')
3
+ const {get, merge} = require('lodash')
4
4
  const {stripHtml} = require('string-strip-html')
5
+ const defaultConfig = require('./posthtml/defaultConfig')
5
6
 
6
7
  const self = {
7
- handleCustomTags: (html, config = {}) => {
8
+ removeCustomTags: (tag, html, config = {}) => {
8
9
  const posthtmlOptions = get(config, 'build.posthtml.options', {})
9
10
 
11
+ const posthtmlPlugin = () => tree => {
12
+ const process = node => {
13
+ if (!node.tag) {
14
+ return node
15
+ }
16
+
17
+ if (node.tag === tag) {
18
+ return {
19
+ tag: false,
20
+ content: ['']
21
+ }
22
+ }
23
+
24
+ if (Array.isArray(tag) && tag.includes(node.tag)) {
25
+ return {
26
+ tag: false,
27
+ content: ['']
28
+ }
29
+ }
30
+
31
+ return node
32
+ }
33
+
34
+ return tree.walk(process)
35
+ }
36
+
37
+ return posthtml([posthtmlPlugin()]).process(html, {...posthtmlOptions, sync: true}).html
38
+ },
39
+ handleCustomTags: (html, config = {}) => {
40
+ const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
41
+
10
42
  const posthtmlPlugin = () => tree => {
11
43
  const process = node => {
12
44
  if (node.tag === 'plaintext') {
@@ -31,16 +63,18 @@ const self = {
31
63
 
32
64
  return posthtml([posthtmlPlugin()]).process(html, {...posthtmlOptions, sync: true}).html
33
65
  },
34
- generate: async (html, destination, config) => {
66
+ generate: async (html, destination, config = {}) => {
35
67
  const configDestinationPath = get(config, 'destination.path')
36
68
  const extension = get(config, 'destination.extension', 'txt')
37
69
 
38
- const plaintext = stripHtml(html, {
70
+ const strippedHTML = self.removeCustomTags('not-plaintext', html, config)
71
+
72
+ const plaintext = stripHtml(strippedHTML, {
39
73
  dumpLinkHrefsNearby: {
40
74
  enabled: true
41
75
  },
42
- stripTogetherWithTheirContents: ['script', 'style', 'xml', 'not-plaintext'],
43
- ...get(config, 'options', {})
76
+ stripTogetherWithTheirContents: ['script', 'style', 'xml'],
77
+ ...config
44
78
  }).result
45
79
 
46
80
  html = self.handleCustomTags(html, config)
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ recognizeNoValueAttribute: true
3
+ }
@@ -4,6 +4,7 @@ const {get, merge} = require('lodash')
4
4
  const fetch = require('posthtml-fetch')
5
5
  const layouts = require('posthtml-extend')
6
6
  const modules = require('posthtml-modules')
7
+ const defaultConfig = require('./defaultConfig')
7
8
  const expressions = require('posthtml-expressions')
8
9
 
9
10
  module.exports = async (html, config) => {
@@ -14,7 +15,7 @@ module.exports = async (html, config) => {
14
15
  const modulesRoot = modulesOptions.root || './'
15
16
  const modulesFrom = modulesOptions.from || `${modulesRoot}/fake`
16
17
 
17
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
18
+ const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
18
19
  const posthtmlPlugins = get(config, 'build.posthtml.plugins', [])
19
20
 
20
21
  const expressionsOptions = merge({strictMode: false}, get(config, 'build.posthtml.expressions', {}))
@@ -31,11 +31,19 @@ module.exports = {
31
31
  }
32
32
 
33
33
  // Merge user's Tailwind config on top of a 'base' config
34
+ const layoutsRoot = get(maizzleConfig, 'build.layouts.root')
35
+ const componentsRoot = get(maizzleConfig, 'build.components.root')
36
+
34
37
  const config = merge({
35
38
  important: true,
36
39
  content: {
37
40
  files: [
38
- './src/**/*.*',
41
+ typeof layoutsRoot === 'string' && layoutsRoot ?
42
+ `${layoutsRoot}/**/*.html`.replace(/\/\//g, '/') :
43
+ './src/layouts/**/*.html',
44
+ typeof componentsRoot === 'string' && componentsRoot ?
45
+ `${componentsRoot}/**/*.html`.replace(/\/\//g, '/') :
46
+ './src/components/**/*.html',
39
47
  {raw: html, extension: 'html'}
40
48
  ]
41
49
  }
@@ -46,7 +54,6 @@ module.exports = {
46
54
  config.content = {
47
55
  files: [
48
56
  ...config.content,
49
- './src/**/*.*',
50
57
  {raw: html, extension: 'html'}
51
58
  ]
52
59
  }
@@ -1,9 +1,10 @@
1
1
  const posthtml = require('posthtml')
2
2
  const parseAttrs = require('posthtml-attrs-parser')
3
- const {get, forEach, intersection, keys, isEmpty} = require('lodash')
3
+ const defaultConfig = require('../generators/posthtml/defaultConfig')
4
+ const {get, merge, forEach, intersection, keys, isEmpty} = require('lodash')
4
5
 
5
6
  module.exports = async (html, config = {}, direct = false) => {
6
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
7
+ const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
7
8
  const attributes = get(config, 'inlineCSS.attributeToStyle', false)
8
9
 
9
10
  if (typeof attributes === 'boolean' && attributes) {
@@ -1,7 +1,33 @@
1
1
  const posthtml = require('posthtml')
2
2
  const isUrl = require('is-url-superb')
3
3
  const baseUrl = require('posthtml-base-url')
4
- const {get, isObject, isEmpty} = require('lodash')
4
+ const {get, merge, isObject, isEmpty} = require('lodash')
5
+ const defaultConfig = require('../generators/posthtml/defaultConfig')
6
+
7
+ module.exports = async (html, config = {}, direct = false) => {
8
+ const url = direct ? config : get(config, 'baseURL')
9
+ const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
10
+
11
+ // Handle `baseUrl` as a string
12
+ if (typeof url === 'string' && url.length > 0) {
13
+ html = rewriteVMLs(html, url)
14
+
15
+ return posthtml([
16
+ baseUrl({url, allTags: true, styleTag: true, inlineCss: true})
17
+ ])
18
+ .process(html, posthtmlOptions)
19
+ .then(result => result.html)
20
+ }
21
+
22
+ // Handle `baseUrl` as an object
23
+ if (isObject(url) && !isEmpty(url)) {
24
+ html = rewriteVMLs(html, url.url)
25
+
26
+ return posthtml([baseUrl(url)]).process(html, posthtmlOptions).then(result => result.html)
27
+ }
28
+
29
+ return html
30
+ }
5
31
 
6
32
  /**
7
33
  * VML backgrounds must be handled with regex because
@@ -42,28 +68,3 @@ const rewriteVMLs = (html, url) => {
42
68
 
43
69
  return html
44
70
  }
45
-
46
- module.exports = async (html, config = {}, direct = false) => {
47
- const url = direct ? config : get(config, 'baseURL')
48
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
49
-
50
- // Handle `baseUrl` as a string
51
- if (typeof url === 'string' && url.length > 0) {
52
- html = rewriteVMLs(html, url)
53
-
54
- return posthtml([
55
- baseUrl({url, allTags: true, styleTag: true, inlineCss: true})
56
- ])
57
- .process(html, posthtmlOptions)
58
- .then(result => result.html)
59
- }
60
-
61
- // Handle `baseUrl` as an object
62
- if (isObject(url) && !isEmpty(url)) {
63
- html = rewriteVMLs(html, url.url)
64
-
65
- return posthtml([baseUrl(url)]).process(html, posthtmlOptions).then(result => result.html)
66
- }
67
-
68
- return html
69
- }
@@ -1,13 +1,14 @@
1
1
  const posthtml = require('posthtml')
2
- const {get, isObject} = require('lodash')
2
+ const {get, merge, isObject} = require('lodash')
3
3
  const addAttributes = require('posthtml-extra-attributes')
4
+ const defaultConfig = require('../generators/posthtml/defaultConfig')
4
5
 
5
6
  module.exports = async (html, config = {}, direct = false) => {
6
7
  if (get(config, 'extraAttributes') === false) {
7
8
  return html
8
9
  }
9
10
 
10
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
11
+ const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
11
12
 
12
13
  let attributes = {
13
14
  table: {
@@ -5,13 +5,14 @@ const PostCSS = require('../../generators/postcss')
5
5
  const posthtmlContent = require('posthtml-content')
6
6
  const Tailwind = require('../../generators/tailwindcss')
7
7
  const safeClassNames = require('posthtml-safe-class-names')
8
+ const defaultConfig = require('../../generators/posthtml/defaultConfig')
8
9
 
9
10
  module.exports = async (html, config = {}, direct = false) => {
10
11
  const filters = direct ?
11
12
  merge(defaultFilters, config) :
12
13
  merge(defaultFilters, get(config, 'filters', {}))
13
14
 
14
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
15
+ const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
15
16
 
16
17
  /**
17
18
  * Compile CSS in <style {post|tailwind}css> tags
@@ -22,11 +23,21 @@ module.exports = async (html, config = {}, direct = false) => {
22
23
  filters.postcss = css => PostCSS.process(css, maizzleConfig)
23
24
  filters.tailwindcss = css => Tailwind.compile(css, html, tailwindConfig, maizzleConfig)
24
25
 
25
- return posthtml([
26
+ const posthtmlPlugins = [
26
27
  styleDataEmbed(),
27
- posthtmlContent(filters),
28
- safeClassNames()
29
- ])
28
+ posthtmlContent(filters)
29
+ ]
30
+
31
+ if (get(config, 'safeClassNames') !== false) {
32
+ posthtmlPlugins.push(safeClassNames({
33
+ replacements: {
34
+ '{': '{',
35
+ '}': '}'
36
+ }
37
+ }))
38
+ }
39
+
40
+ return posthtml(posthtmlPlugins)
30
41
  .process(html, posthtmlOptions)
31
42
  .then(result => result.html)
32
43
  }
@@ -49,10 +49,10 @@ exports.prettify = (html, config) => prettify(html, config, true)
49
49
  exports.ensureSixHEX = (html, config) => ensureSixHEX(html, config)
50
50
  exports.withFilters = (html, config) => filters(html, config, true)
51
51
  exports.addURLParams = (html, config) => addURLParams(html, config, true)
52
- exports.preventWidows = (html, config) => preventWidows(html, config, true)
52
+ exports.preventWidows = (html, config) => preventWidows(html, config)
53
53
  exports.replaceStrings = (html, config) => replaceStrings(html, config, true)
54
54
  exports.safeClassNames = (html, config) => safeClassNames(html, config, true)
55
- exports.removeUnusedCSS = (html, config) => removeUnusedCSS(html, config, true)
55
+ exports.removeUnusedCSS = (html, config) => removeUnusedCSS(html, config)
56
56
  exports.removeAttributes = (html, config) => removeAttributes(html, config, true)
57
57
  exports.attributeToStyle = (html, config) => attributeToStyle(html, config, true)
58
58
  exports.removeInlineSizes = (html, config) => removeInlineSizes(html, config, true)
@@ -1,6 +1,7 @@
1
1
  const posthtml = require('posthtml')
2
2
  const {get, merge} = require('lodash')
3
3
  const markdown = require('posthtml-markdownit')
4
+ const defaultConfig = require('../generators/posthtml/defaultConfig')
4
5
 
5
6
  module.exports = async (html, config = {}, direct = false) => {
6
7
  if (get(config, 'markdown') === false) {
@@ -8,7 +9,7 @@ module.exports = async (html, config = {}, direct = false) => {
8
9
  }
9
10
 
10
11
  const userMarkdownOptions = direct ? config : get(config, 'markdown', {})
11
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
12
+ const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
12
13
  const markdownOptions = merge({markdownit: {html: true}}, userMarkdownOptions)
13
14
 
14
15
  return posthtml([
@@ -1,10 +1,11 @@
1
- const {get} = require('lodash')
2
1
  const posthtml = require('posthtml')
2
+ const {get, merge} = require('lodash')
3
3
  const outlook = require('posthtml-mso')
4
+ const defaultConfig = require('../generators/posthtml/defaultConfig')
4
5
 
5
6
  module.exports = async (html, config) => {
6
7
  const outlookOptions = get(config, 'build.posthtml.outlook', {})
7
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
8
+ const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
8
9
 
9
10
  return posthtml([outlook({...outlookOptions})]).process(html, posthtmlOptions).then(result => result.html)
10
11
  }
@@ -1,13 +1,81 @@
1
- const {get} = require('lodash')
2
1
  const posthtml = require('posthtml')
3
- const preventWidows = require('prevent-widows')
2
+ const {get, merge, isEmpty} = require('lodash')
3
+ const {removeWidows} = require('string-remove-widows')
4
+ const defaultConfig = require('../generators/posthtml/defaultConfig')
4
5
 
5
- module.exports = async (html, config = {}, direct = false) => {
6
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
6
+ module.exports = async (html, config = {}) => {
7
+ if (isEmpty(config)) {
8
+ return removeWidows(html).res
9
+ }
10
+
11
+ const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
12
+
13
+ return posthtml([removeWidowsPlugin(config)]).process(html, posthtmlOptions).then(result => result.html)
14
+ }
15
+
16
+ const removeWidowsPlugin = options => tree => {
17
+ const {attrName = 'prevent-widows', ...removeWidowsOptions} = get(options, 'widowWords', options)
18
+
19
+ removeWidowsOptions.minWordCount = removeWidowsOptions.minWordCount || 3
20
+
21
+ // Ignore defaults
22
+ const mappings = [
23
+ // Jinja-like
24
+ {
25
+ heads: '{{',
26
+ tails: '}}'
27
+ },
28
+ {
29
+ heads: ['{% if', '{%- if'],
30
+ tails: ['{% endif', '{%- endif']
31
+ },
32
+ {
33
+ heads: ['{% for', '{%- for'],
34
+ tails: ['{% endfor', '{%- endfor']
35
+ },
36
+ {
37
+ heads: ['{%', '{%-'],
38
+ tails: ['%}', '-%}']
39
+ },
40
+ {
41
+ heads: '{#',
42
+ tails: '#}'
43
+ },
44
+ // ASP/Hexo-like
45
+ {
46
+ heads: ['<%', '<%=', '<%-'],
47
+ tails: ['%>', '=%>', '-%>']
48
+ },
49
+ // MSO comments
50
+ {
51
+ heads: '<!--[',
52
+ tails: ']>'
53
+ },
54
+ // <![endif]-->
55
+ {
56
+ heads: '<![',
57
+ tails: ']--><'
58
+ }
59
+ ]
60
+
61
+ if (Array.isArray(removeWidowsOptions.ignore)) {
62
+ removeWidowsOptions.ignore.forEach(pair => mappings.push(pair))
63
+ }
64
+
65
+ if (typeof removeWidowsOptions.ignore !== 'string') {
66
+ removeWidowsOptions.ignore = mappings
67
+ }
68
+
69
+ const process = node => {
70
+ if (node.attrs && Object.keys(node.attrs).includes(attrName)) {
71
+ const widowsRemovedString = removeWidows(tree.render(node.content), removeWidowsOptions).res
72
+
73
+ node.content = tree.render(tree.parser(widowsRemovedString))
74
+ node.attrs[attrName] = false
75
+ }
7
76
 
8
- if (direct) {
9
- return preventWidows(html)
77
+ return node
10
78
  }
11
79
 
12
- return posthtml([preventWidows.posthtml()]).process(html, posthtmlOptions).then(result => result.html)
80
+ return tree.walk(process)
13
81
  }
@@ -1,17 +1,56 @@
1
- const {get} = require('lodash')
2
1
  const posthtml = require('posthtml')
3
- const removeAttributes = require('posthtml-remove-attributes')
2
+ const {get, merge} = require('lodash')
3
+ const defaultConfig = require('../generators/posthtml/defaultConfig')
4
4
 
5
5
  module.exports = async (html, config = {}, direct = false) => {
6
6
  const attributes = direct ? (Array.isArray(config) ? [...config] : []) : get(config, 'removeAttributes', [])
7
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
7
+ const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
8
8
 
9
- attributes.push({name: 'style'}, {name: 'class'})
9
+ attributes.push('style', 'class')
10
10
 
11
- // Allow omitting `value` key when removing empty attributes
12
- attributes.forEach(attr => {
13
- attr.value = attr.value || ''
14
- })
11
+ html = await posthtml([
12
+ removeAttributes(attributes, posthtmlOptions)
13
+ ]).process(html, posthtmlOptions).then(result => result.html)
15
14
 
16
- return posthtml([removeAttributes(attributes)]).process(html, posthtmlOptions).then(result => result.html)
15
+ return html
16
+ }
17
+
18
+ /**
19
+ * Remove empty attributes with PostHTML
20
+ *
21
+ * Condition 1:
22
+ * `boolean` is for attributes without ="" (respects `recognizeNoValueAttribute` in PostHTML)
23
+ * `''` if the attribute included ="", i.e. style=""
24
+ *
25
+ * Condition 2: attribute value is a string and matches the one on the node
26
+ *
27
+ * Condition 3: same as 2, but for regular expressions
28
+ */
29
+ const removeAttributes = (attributes = {}, posthtmlOptions = {}) => tree => {
30
+ const process = node => {
31
+ const normalizedAttrs = attributes.map(attribute => {
32
+ return {
33
+ name: get(attribute, 'name', typeof attribute === 'string' ? attribute : false),
34
+ value: get(attribute, 'value', get(posthtmlOptions, 'recognizeNoValueAttributes', true))
35
+ }
36
+ })
37
+
38
+ if (node.attrs) {
39
+ normalizedAttrs.forEach(attr => {
40
+ const targetAttrValue = get(node.attrs, attr.name)
41
+
42
+ if (
43
+ typeof targetAttrValue === 'boolean' || targetAttrValue === '' ||
44
+ (typeof attr.value === 'string' && node.attrs[attr.name] === attr.value) ||
45
+ (attr.value instanceof RegExp && attr.value.test(node.attrs[attr.name]))
46
+ ) {
47
+ node.attrs[attr.name] = false
48
+ }
49
+ })
50
+ }
51
+
52
+ return node
53
+ }
54
+
55
+ return tree.walk(process)
17
56
  }
@@ -1,9 +1,11 @@
1
1
  const posthtml = require('posthtml')
2
- const {get, isEmpty} = require('lodash')
2
+ const {get, merge, isEmpty} = require('lodash')
3
3
  const parseAttrs = require('posthtml-attrs-parser')
4
+ const {toStyleString} = require('../utils/helpers')
5
+ const defaultConfig = require('../generators/posthtml/defaultConfig')
4
6
 
5
7
  module.exports = async (html, config = {}, direct = false) => {
6
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
8
+ const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
7
9
 
8
10
  if (isEmpty(config)) {
9
11
  return posthtml([removeInlineBGColor()]).process(html, posthtmlOptions).then(result => result.html)
@@ -40,9 +42,11 @@ const removeInlineBGColor = (options = {}) => tree => {
40
42
  })
41
43
 
42
44
  if (attrs.style && attrs.style['background-color']) {
43
- attrs.bgcolor = attrs.style['background-color']
45
+ node.attrs.bgcolor = attrs.style['background-color']
46
+
44
47
  delete attrs.style['background-color']
45
- node.attrs = attrs.compose()
48
+
49
+ node.attrs.style = toStyleString(attrs.style)
46
50
  }
47
51
 
48
52
  return node
@@ -1,12 +1,15 @@
1
1
  const posthtml = require('posthtml')
2
- const {get, isEmpty} = require('lodash')
2
+ const {get, merge, isEmpty} = require('lodash')
3
3
  const parseAttrs = require('posthtml-attrs-parser')
4
+ const {toStyleString} = require('../utils/helpers')
5
+ const defaultConfig = require('../generators/posthtml/defaultConfig')
4
6
 
5
7
  module.exports = async (html, config = {}, direct = false) => {
6
8
  const settings = direct ? config : get(config, 'inlineCSS.keepOnlyAttributeSizes', {})
7
9
 
8
10
  if (!isEmpty(settings)) {
9
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
11
+ const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
12
+
10
13
  return posthtml([removeInlineSizes(settings)]).process(html, posthtmlOptions).then(result => result.html)
11
14
  }
12
15
 
@@ -26,14 +29,14 @@ const removeInlineSizes = (mappings = {}) => tree => {
26
29
  }
27
30
 
28
31
  tags.forEach(() => {
29
- if (attrs.style) {
32
+ if (get(node, 'attrs.style')) {
30
33
  delete attrs.style[attribute]
34
+
35
+ node.attrs.style = toStyleString(attrs.style)
31
36
  }
32
37
  })
33
38
  })
34
39
 
35
- node.attrs = attrs.compose()
36
-
37
40
  return node
38
41
  }
39
42
 
@@ -1,19 +1,21 @@
1
- const {get, has, remove} = require('lodash')
2
1
  const postcss = require('postcss')
3
2
  const posthtml = require('posthtml')
3
+ const {get, merge, has, remove} = require('lodash')
4
4
  const parseAttrs = require('posthtml-attrs-parser')
5
5
  const matchHelper = require('posthtml-match-helper')
6
+ const defaultConfig = require('../generators/posthtml/defaultConfig')
6
7
 
7
8
  module.exports = async (html, config = {}) => {
8
9
  if (get(config, 'removeInlinedClasses') === false) {
9
10
  return html
10
11
  }
11
12
 
12
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
13
- return posthtml([plugin()]).process(html, posthtmlOptions).then(result => result.html)
13
+ const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
14
+
15
+ return posthtml([plugin(posthtmlOptions)]).process(html, posthtmlOptions).then(result => result.html)
14
16
  }
15
17
 
16
- const plugin = () => tree => {
18
+ const plugin = posthtmlOptions => tree => {
17
19
  const process = node => {
18
20
  // For each style tag...
19
21
  if (node.tag === 'style') {
@@ -57,12 +59,24 @@ const plugin = () => tree => {
57
59
 
58
60
  n.attrs = parsedAttrs.compose()
59
61
 
62
+ // Fix issue with .compose() automatically quoting attributes with no values
63
+ Object.entries(n.attrs).forEach(([name, value]) => {
64
+ if (value === '' && get(posthtmlOptions, 'recognizeNoValueAttribute') === true) {
65
+ n.attrs[name] = true
66
+ }
67
+ })
68
+
60
69
  return n
61
70
  })
62
71
  } catch {}
63
72
  })
64
73
 
65
- node.content = root.toString()
74
+ node.content = root.toString().trim()
75
+
76
+ // Remove <style> tag if it ends up empty after processing
77
+ if (node.content.length === 0) {
78
+ node.tag = false
79
+ }
66
80
  }
67
81
 
68
82
  return node
@@ -1,13 +1,11 @@
1
1
  const {comb} = require('email-comb')
2
- const {get, isEmpty} = require('lodash')
2
+ const {get, merge} = require('lodash')
3
3
 
4
- module.exports = async (html, config = {}, direct = false) => {
4
+ module.exports = async (html, config = {}) => {
5
5
  if (get(config, 'removeUnusedCSS') === false) {
6
6
  return html
7
7
  }
8
8
 
9
- const options = direct ? config : get(config, 'removeUnusedCSS', {})
10
-
11
9
  const safelist = [
12
10
  '*body*', // Gmail
13
11
  '.gmail*', // Gmail
@@ -26,15 +24,17 @@ module.exports = async (html, config = {}, direct = false) => {
26
24
  '.lang*' // Fenced code blocks
27
25
  ]
28
26
 
29
- if (typeof options === 'boolean' && options) {
30
- return comb(html, {whitelist: safelist}).result
27
+ const defaultOptions = {
28
+ backend: [
29
+ {heads: '{{', tails: '}}'},
30
+ {heads: '{%', tails: '%}'}
31
+ ],
32
+ whitelist: [...get(config, 'whitelist', []), ...safelist]
31
33
  }
32
34
 
33
- if (!isEmpty(options)) {
34
- options.whitelist = [...get(options, 'whitelist', []), ...safelist]
35
-
36
- return comb(html, options).result
37
- }
35
+ const options = typeof config === 'boolean' && config ?
36
+ defaultOptions :
37
+ merge(defaultOptions, get(config, 'removeUnusedCSS', config))
38
38
 
39
- return html
39
+ return comb(html, options).result
40
40
  }
@@ -1,4 +1,4 @@
1
- const {isEmpty, get} = require('lodash')
1
+ const {get, isEmpty} = require('lodash')
2
2
 
3
3
  module.exports = async (html, config = {}, direct = false) => {
4
4
  const replacements = direct ? config : get(config, 'replaceStrings', {})