@maizzle/framework 4.1.2 → 4.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maizzle/framework",
3
- "version": "4.1.2",
3
+ "version": "4.2.1",
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",
@@ -30,7 +30,7 @@
30
30
  "access": "public"
31
31
  },
32
32
  "scripts": {
33
- "test": "c8 ava -s",
33
+ "test": "c8 ava",
34
34
  "pretest": "xo",
35
35
  "style": "xo",
36
36
  "release": "np"
@@ -3,7 +3,6 @@ const fs = require('fs-extra')
3
3
  const glob = require('glob-promise')
4
4
  const {get, isEmpty, merge} = require('lodash')
5
5
  const {asyncForEach} = require('../../utils/helpers')
6
- const removePlaintextTags = require('../../transformers/plaintext')
7
6
 
8
7
  const Config = require('../config')
9
8
  const Tailwind = require('../tailwindcss')
@@ -27,9 +26,9 @@ module.exports = async (env, spinner, config) => {
27
26
  const parsed = []
28
27
  let files = []
29
28
 
30
- const css = (typeof get(config, 'tailwind.compiled') === 'string')
31
- ? config.tailwind.compiled
32
- : await Tailwind.compile('', '', {}, config, spinner)
29
+ const css = (typeof get(config, 'build.tailwind.compiled') === 'string')
30
+ ? config.build.tailwind.compiled
31
+ : await Tailwind.compile('', '', {}, config)
33
32
 
34
33
  // Parse each template config object
35
34
  await asyncForEach(templatesConfig, async templateConfig => {
@@ -104,6 +103,7 @@ module.exports = async (env, spinner, config) => {
104
103
 
105
104
  try {
106
105
  const compiled = await render(html, {
106
+ useFileConfig: true,
107
107
  maizzle: {
108
108
  ...config,
109
109
  env
@@ -123,7 +123,8 @@ module.exports = async (env, spinner, config) => {
123
123
  * tags from the markup before outputting the file.
124
124
  */
125
125
 
126
- const plaintextConfig = get(templateConfig, 'plaintext')
126
+ // Check if plaintext: true globally, fallback to template's front matter
127
+ const plaintextConfig = get(templateConfig, 'plaintext', get(compiled.config, 'plaintext', false))
127
128
  const plaintextPath = get(plaintextConfig, 'destination.path', config.permalink || file)
128
129
 
129
130
  if (Boolean(plaintextConfig) || !isEmpty(plaintextConfig)) {
@@ -133,11 +134,12 @@ module.exports = async (env, spinner, config) => {
133
134
  plaintextPath,
134
135
  merge(plaintextConfig, {filepath: file})
135
136
  )
136
- .then(({plaintext, destination}) => fs.outputFile(destination, plaintext))
137
+ .then(async ({html, plaintext, destination}) => {
138
+ compiled.html = html
139
+ await fs.outputFile(destination, plaintext)
140
+ })
137
141
  }
138
142
 
139
- compiled.html = removePlaintextTags(compiled.html, config)
140
-
141
143
  /**
142
144
  * Output file
143
145
  */
@@ -17,7 +17,9 @@ module.exports = async (html, options) => {
17
17
  throw new RangeError('received empty string')
18
18
  }
19
19
 
20
- const fileConfig = await Config.getMerged(process.env.NODE_ENV)
20
+ const fileConfig = get(options, 'useFileConfig')
21
+ ? await Config.getMerged(process.env.NODE_ENV)
22
+ : {}
21
23
 
22
24
  let config = merge(fileConfig, get(options, 'maizzle', {}))
23
25
 
@@ -1,49 +1,84 @@
1
1
  const path = require('path')
2
2
  const {get} = require('lodash')
3
+ const posthtml = require('posthtml')
3
4
  const {stripHtml} = require('string-strip-html')
4
5
 
5
- module.exports.generate = async (html, destination, config) => {
6
- const configDestinationPath = get(config, 'destination.path')
7
- const extension = get(config, 'destination.extension', 'txt')
6
+ const self = {
7
+ handleCustomTags: (html, config = {}) => {
8
+ const posthtmlOptions = get(config, 'build.posthtml.options', {})
8
9
 
9
- const plaintext = stripHtml(html, {
10
- dumpLinkHrefsNearby: {
11
- enabled: true
12
- },
13
- ...get(config, 'options', {})
14
- }).result
10
+ const posthtmlPlugin = () => tree => {
11
+ const process = node => {
12
+ if (node.tag === 'plaintext') {
13
+ return {
14
+ tag: false,
15
+ content: ['']
16
+ }
17
+ }
15
18
 
16
- // If we set plaintext.destination.path in config/fm
17
- if (configDestinationPath) {
18
- /**
19
- * Using a file path will generate a single plaintext file,
20
- * no matter how many templates there are.
21
- *
22
- * It will be based on the last-processed template.
23
- */
24
- if (path.extname(configDestinationPath)) {
25
- destination = configDestinationPath
19
+ if (node.tag === 'not-plaintext') {
20
+ return {
21
+ tag: false,
22
+ content: tree.render(node.content)
23
+ }
24
+ }
25
+
26
+ return node
27
+ }
28
+
29
+ return tree.walk(process)
30
+ }
31
+
32
+ return posthtml([posthtmlPlugin()]).process(html, {...posthtmlOptions, sync: true}).html
33
+ },
34
+ generate: async (html, destination, config) => {
35
+ const configDestinationPath = get(config, 'destination.path')
36
+ const extension = get(config, 'destination.extension', 'txt')
37
+
38
+ const plaintext = stripHtml(html, {
39
+ dumpLinkHrefsNearby: {
40
+ enabled: true
41
+ },
42
+ stripTogetherWithTheirContents: ['script', 'style', 'xml', 'not-plaintext'],
43
+ ...get(config, 'options', {})
44
+ }).result
45
+
46
+ html = self.handleCustomTags(html, config)
26
47
 
27
- return {plaintext, destination}
48
+ // If we set plaintext.destination.path in config/fm
49
+ if (configDestinationPath) {
50
+ /**
51
+ * Using a file path will generate a single plaintext file,
52
+ * no matter how many templates there are.
53
+ *
54
+ * It will be based on the last-processed template.
55
+ */
56
+ if (path.extname(configDestinationPath)) {
57
+ destination = configDestinationPath
58
+
59
+ return {html, plaintext, destination}
60
+ }
61
+
62
+ /**
63
+ * Using a directory-like path for plaintext.destination.path
64
+ */
65
+ destination = path.join(configDestinationPath, path.basename(config.filepath, path.extname(config.filepath)) + '.' + extension)
66
+
67
+ return {html, plaintext, destination}
28
68
  }
29
69
 
30
70
  /**
31
- * Using a directory-like path for plaintext.destination.path
71
+ * Use template's `permalink` Front Matter key,
72
+ * fall back to the original `destination`.
32
73
  */
33
- destination = path.join(configDestinationPath, path.basename(config.filepath, path.extname(config.filepath)) + '.' + extension)
34
-
35
- return {plaintext, destination}
36
- }
74
+ destination = get(config, 'permalink', destination)
37
75
 
38
- /**
39
- * Use template's `permalink` Front Matter key,
40
- * fall back to the original `destination`.
41
- */
42
- destination = get(config, 'permalink', destination)
76
+ if (typeof destination === 'string') {
77
+ destination = path.join(path.dirname(destination), path.basename(destination, path.extname(destination)) + '.' + extension)
78
+ }
43
79
 
44
- if (typeof destination === 'string') {
45
- destination = path.join(path.dirname(destination), path.basename(destination, path.extname(destination)) + '.' + extension)
80
+ return {html, plaintext, destination}
46
81
  }
47
-
48
- return {plaintext, destination}
49
82
  }
83
+
84
+ module.exports = self
@@ -1,4 +1,3 @@
1
- const path = require('path')
2
1
  const {get} = require('lodash')
3
2
  const postcss = require('postcss')
4
3
  const postcssImport = require('postcss-import')
@@ -6,11 +5,9 @@ const postcssNested = require('tailwindcss/nesting')
6
5
  const mergeLonghand = require('postcss-merge-longhand')
7
6
 
8
7
  module.exports = {
9
- process: async (css = '', maizzleConfig = {}, spinner = null) => {
10
- const userFilePath = get(maizzleConfig, 'build.tailwind.css', path.join(process.cwd(), 'src/css/tailwind.css'))
11
-
8
+ process: async (css = '', maizzleConfig = {}) => {
12
9
  return postcss([
13
- postcssImport({path: path.dirname(userFilePath)}),
10
+ postcssImport(),
14
11
  postcssNested(),
15
12
  maizzleConfig.env === 'local' ? () => {} : mergeLonghand(),
16
13
  ...get(maizzleConfig, 'build.postcss.plugins', [])
@@ -18,12 +15,7 @@ module.exports = {
18
15
  .process(css, {from: undefined})
19
16
  .then(result => result.css)
20
17
  .catch(error => {
21
- console.error(error)
22
- if (spinner) {
23
- spinner.stop()
24
- }
25
-
26
- throw new Error(`PostCSS processing failed`)
18
+ throw new SyntaxError(error)
27
19
  })
28
20
  }
29
21
  }
@@ -47,6 +47,7 @@ module.exports = async (html, config) => {
47
47
  )
48
48
  ),
49
49
  modules({
50
+ expressions: expressionsOptions,
50
51
  parser: posthtmlOptions,
51
52
  attributeAsLocals: true,
52
53
  from: modulesFrom,
@@ -9,7 +9,7 @@ const mergeLonghand = require('postcss-merge-longhand')
9
9
  const {get, isObject, isEmpty, merge} = require('lodash')
10
10
 
11
11
  module.exports = {
12
- compile: async (css = '', html = '', tailwindConfig = {}, maizzleConfig = {}, spinner = null) => {
12
+ compile: async (css = '', html = '', tailwindConfig = {}, maizzleConfig = {}) => {
13
13
  tailwindConfig = (isObject(tailwindConfig) && !isEmpty(tailwindConfig)) ? tailwindConfig : get(maizzleConfig, 'build.tailwind.config', 'tailwind.config.js')
14
14
 
15
15
  // Compute the Tailwind config to use
@@ -89,28 +89,27 @@ module.exports = {
89
89
  const userFilePath = get(maizzleConfig, 'build.tailwind.css', path.join(process.cwd(), 'src/css/tailwind.css'))
90
90
  const userFileExists = await fs.pathExists(userFilePath)
91
91
 
92
+ const toProcess = [
93
+ postcssNested(),
94
+ tailwindcss(config),
95
+ maizzleConfig.env === 'local' ? () => {} : mergeLonghand(),
96
+ ...get(maizzleConfig, 'build.postcss.plugins', [])
97
+ ]
98
+
92
99
  if (userFileExists) {
93
100
  css = await fs.readFile(path.resolve(userFilePath), 'utf8') + css
101
+ toProcess.unshift(
102
+ postcssImport({path: path.dirname(userFilePath)})
103
+ )
94
104
  } else {
95
- css = `@import "tailwindcss/components"; @import "tailwindcss/utilities"; ${css}`
105
+ css = `@tailwind components; @tailwind utilities; ${css}`
96
106
  }
97
107
 
98
- return postcss([
99
- postcssImport({path: path.dirname(userFilePath)}),
100
- postcssNested(),
101
- tailwindcss(config),
102
- maizzleConfig.env === 'local' ? () => {} : mergeLonghand(),
103
- ...get(maizzleConfig, 'build.postcss.plugins', [])
104
- ])
108
+ return postcss([...toProcess])
105
109
  .process(css, {from: undefined})
106
110
  .then(result => result.css)
107
111
  .catch(error => {
108
- console.error(error)
109
- if (spinner) {
110
- spinner.stop()
111
- }
112
-
113
- throw new Error(`Tailwind CSS compilation failed`)
112
+ throw new SyntaxError(error)
114
113
  })
115
114
  }
116
115
  }
@@ -19,7 +19,7 @@ module.exports = async (html, config = {}, direct = false) => {
19
19
  attributesToStyle({
20
20
  attributes: Array.isArray(config) ? config : []
21
21
  })
22
- ]).process(html).then(result => result.html)
22
+ ]).process(html, posthtmlOptions).then(result => result.html)
23
23
  }
24
24
 
25
25
  return html
@@ -32,54 +32,54 @@ const attributesToStyle = (options = {}) => tree => {
32
32
  const nodeAttributes = parseAttrs(node.attrs)
33
33
  const matches = intersection(keys(nodeAttributes), options.attributes)
34
34
  const nodeStyle = get(node.attrs, 'style')
35
- const csstoInline = []
35
+ const cssToInline = []
36
36
 
37
37
  forEach(matches, attribute => {
38
38
  let value = get(node.attrs, attribute)
39
39
 
40
40
  switch (attribute) {
41
41
  case 'bgcolor':
42
- csstoInline.push(`background-color: ${value}`)
42
+ cssToInline.push(`background-color: ${value}`)
43
43
  break
44
44
 
45
45
  case 'background':
46
- csstoInline.push(`background-image: url('${value}')`)
46
+ cssToInline.push(`background-image: url('${value}')`)
47
47
  break
48
48
 
49
49
  case 'width':
50
50
  value = Number.parseInt(value, 10) + (value.match(/px|%/) || 'px')
51
- csstoInline.push(`width: ${value}`)
51
+ cssToInline.push(`width: ${value}`)
52
52
  break
53
53
 
54
54
  case 'height':
55
55
  value = Number.parseInt(value, 10) + (value.match(/px|%/) || 'px')
56
- csstoInline.push(`height: ${value}`)
56
+ cssToInline.push(`height: ${value}`)
57
57
  break
58
58
 
59
59
  case 'align':
60
60
  if (node.tag !== 'table') {
61
- return csstoInline.push(`text-align: ${value}`)
61
+ return cssToInline.push(`text-align: ${value}`)
62
62
  }
63
63
 
64
64
  if (['left', 'right'].includes(value)) {
65
- csstoInline.push(`float: ${value}`)
65
+ cssToInline.push(`float: ${value}`)
66
66
  }
67
67
 
68
68
  if (value === 'center') {
69
- csstoInline.push('margin-left: auto', 'margin-right: auto')
69
+ cssToInline.push('margin-left: auto', 'margin-right: auto')
70
70
  }
71
71
 
72
72
  break
73
73
 
74
74
  case 'valign':
75
- csstoInline.push(`vertical-align: ${value}`)
75
+ cssToInline.push(`vertical-align: ${value}`)
76
76
  break
77
77
 
78
78
  // No default
79
79
  }
80
80
  })
81
81
 
82
- nodeAttributes.style = nodeStyle ? `${nodeStyle} ${csstoInline.join('; ')}` : `${csstoInline.join('; ')}`
82
+ nodeAttributes.style = nodeStyle ? `${nodeStyle} ${cssToInline.join('; ')}` : `${cssToInline.join('; ')}`
83
83
 
84
84
  node.attrs = nodeAttributes.compose()
85
85
 
@@ -6,5 +6,5 @@ module.exports = async (html, config) => {
6
6
  const outlookOptions = get(config, 'build.posthtml.outlook', {})
7
7
  const posthtmlOptions = get(config, 'build.posthtml.options', {})
8
8
 
9
- return posthtml([outlook({...outlookOptions})]).process(html, {...posthtmlOptions}).then(result => result.html)
9
+ return posthtml([outlook({...outlookOptions})]).process(html, posthtmlOptions).then(result => result.html)
10
10
  }
@@ -6,11 +6,11 @@ module.exports = async (html, config = {}, direct = false) => {
6
6
  const urlParameters = direct ? config : get(config, 'urlParameters', {})
7
7
 
8
8
  if (!isEmpty(urlParameters)) {
9
- const {_options, ...parameters} = urlParameters
10
- const tags = _options.tags ?? ['a']
11
- const strict = _options.strict ?? true
12
- const qs = _options.qs ?? {encode: false}
13
9
  const posthtmlOptions = get(config, 'build.posthtml.options', {})
10
+ const {_options, ...parameters} = urlParameters
11
+ const tags = get(_options, 'tags', ['a'])
12
+ const strict = get(_options, 'strict', true)
13
+ const qs = get(_options, 'qs', {encode: false})
14
14
 
15
15
  return posthtml([urlParams({parameters, tags, qs, strict})]).process(html, posthtmlOptions).then(result => result.html)
16
16
  }
@@ -0,0 +1,9 @@
1
+ ---
2
+ plaintext: true
3
+ ---
4
+
5
+ <div>Show in HTML</div>
6
+ <plaintext>Show in plaintext</plaintext>
7
+ <not-plaintext>
8
+ <table><tr><td>Remove from plaintext</td></tr></table>
9
+ </not-plaintext>
@@ -1,2 +1,5 @@
1
1
  <div>Show in HTML</div>
2
2
  <plaintext>Show in plaintext</plaintext>
3
+ <not-plaintext>
4
+ <table><tr><td>Remove from plaintext</td></tr></table>
5
+ </not-plaintext>
@@ -4,5 +4,5 @@ const PostCSS = require('../src/generators/postcss')
4
4
  test('throws on processing error', async t => {
5
5
  await t.throwsAsync(async () => {
6
6
  await PostCSS.process(null, {})
7
- }, {instanceOf: Error, message: 'PostCSS processing failed'})
7
+ }, {instanceOf: SyntaxError})
8
8
  })
@@ -14,27 +14,66 @@ const expected = file => readFile('expected', file)
14
14
  const renderString = (string, options = {}) => Maizzle.render(string, options).then(({html}) => html)
15
15
 
16
16
  test('layouts', async t => {
17
- const source = await fixture('posthtml/layout')
17
+ const source = `---
18
+ greeting: Hello
19
+ ---
18
20
 
19
- const html = await renderString(source, {maizzle: {env: 'maizzle-ci'}})
21
+ <extends src="test/stubs/layouts/basic.html">
22
+ <block name="template">
23
+ Front matter variable: {{ page.greeting }}
24
+ </block>
25
+ </extends>`
20
26
 
21
- t.is(html.trim(), await expected('posthtml/layout'))
27
+ const html = await renderString(source, {
28
+ maizzle: {
29
+ greeting: 'Hello'
30
+ }
31
+ })
32
+
33
+ t.is(html.trim(), `Front matter variable: Hello`)
22
34
  })
23
35
 
24
36
  test('inheritance when extending a template', async t => {
25
- const source = await fixture('posthtml/extend-template')
37
+ const source = `---
38
+ template: second
39
+ ---
40
+
41
+ <extends src="test/stubs/template.html">
42
+ <block name="button">Child in second.html</block>
43
+ </extends>`
44
+
26
45
  let html = await renderString(source)
27
46
 
28
47
  html = html.replace(/[^\S\r\n]+$/gm, '').trim()
29
48
 
30
- t.is(html, await expected('posthtml/extend-template'))
49
+ t.is(html, `Parent
50
+ Child in second.html`)
31
51
  })
32
52
 
33
53
  test('components', async t => {
34
- const source = await fixture('posthtml/component')
54
+ const source = `<component
55
+ src="test/stubs/components/component.html"
56
+ text="Example"
57
+ locals='{
58
+ "foo": "bar"
59
+ }'
60
+ >
61
+ Variable from page: [[ page.env ]]
62
+
63
+ <component
64
+ src="test/stubs/components/component.html"
65
+ text="Nested component"
66
+ locals='{
67
+ "foo": "bar (nested)"
68
+ }'
69
+ >
70
+ Variable from page (nested): [[ page.env ]]
71
+ </component>
72
+ </component>`
73
+
35
74
  const options = {
36
75
  maizzle: {
37
- env: 'maizzle-ci',
76
+ env: 'prod',
38
77
  build: {
39
78
  components: {
40
79
  expressions: {
@@ -47,7 +86,19 @@ test('components', async t => {
47
86
 
48
87
  const html = await renderString(source, options)
49
88
 
50
- t.is(html.trim(), await expected('posthtml/component'))
89
+ t.is(html.trim(), `Variable from attribute: Example
90
+
91
+ Variable from locals attribute: bar
92
+
93
+
94
+ Variable from page: prod
95
+
96
+ Variable from attribute: Nested component
97
+
98
+ Variable from locals attribute: bar (nested)
99
+
100
+
101
+ Variable from page (nested): prod`)
51
102
  })
52
103
 
53
104
  test('fetch component', async t => {
@@ -1,12 +1,10 @@
1
1
  const test = require('ava')
2
- const ora = require('ora')
3
2
  const Tailwind = require('../src/generators/tailwindcss')
4
3
 
5
4
  test('throws on compile error', async t => {
6
5
  await t.throwsAsync(async () => {
7
- const spinner = ora('Compiling Tailwind CSS...').start()
8
- await Tailwind.compile('.test {@apply inexistent;}', '<div class="test">Test</a>', {}, {}, spinner)
9
- }, {instanceOf: Error, message: 'Tailwind CSS compilation failed'})
6
+ await Tailwind.compile('.test {@apply inexistent;}', '<div class="test">Test</a>', {}, {})
7
+ }, {instanceOf: SyntaxError})
10
8
  })
11
9
 
12
10
  test('uses defaults if no config specified', async t => {
@@ -94,7 +94,7 @@ test('outputs files at the correct location if multiple template sources are use
94
94
  })
95
95
 
96
96
  t.true(await fs.pathExists(t.context.folder))
97
- t.is(files.length, 3)
97
+ t.is(files.length, 5)
98
98
  })
99
99
 
100
100
  test('copies all files in the `filetypes` option to destination', async t => {
@@ -154,23 +154,49 @@ test('outputs plaintext files', async t => {
154
154
  path: t.context.folder
155
155
  },
156
156
  plaintext: true
157
- },
158
- tailwind: {
159
- config: {
160
- purge: false
157
+ }
158
+ },
159
+ extraAttributes: false
160
+ })
161
+
162
+ t.true(files.includes(`${t.context.folder}/plaintext.txt`))
163
+
164
+ t.is(
165
+ await fs.readFile(`${t.context.folder}/plaintext.txt`, 'utf8'),
166
+ 'Show in HTML\nShow in plaintext'
167
+ )
168
+
169
+ t.is(
170
+ await fs.readFile(`${t.context.folder}/plaintext.html`, 'utf8'),
171
+ '<div>Show in HTML</div>\n\n\n <table><tr><td>Remove from plaintext</td></tr></table>\n\n'
172
+ )
173
+ })
174
+
175
+ test('outputs plaintext files (front matter)', async t => {
176
+ const {files} = await Maizzle.build('maizzle-ci', {
177
+ fail: 'silent',
178
+ build: {
179
+ templates: {
180
+ source: 'test/stubs/plaintext',
181
+ destination: {
182
+ path: t.context.folder
161
183
  }
162
184
  }
163
- }
185
+ },
186
+ extraAttributes: false
164
187
  })
165
188
 
166
- const plaintext = files.filter(file => file.includes('.txt'))
167
- const html = files.filter(file => file.includes('.html'))
168
- const plaintextContent = await fs.readFile(plaintext[0], 'utf8')
169
- const htmlContent = await fs.readFile(html[0], 'utf8')
189
+ t.true(files.includes(`${t.context.folder}/front-matter.txt`))
190
+
191
+ t.is(
192
+ await fs.readFile(`${t.context.folder}/front-matter.txt`, 'utf8'),
193
+ 'Show in HTML\nShow in plaintext'
194
+ )
170
195
 
171
- t.is(plaintext[0], `${t.context.folder}/plaintext.txt`)
172
- t.is(plaintextContent, 'Show in HTML\nShow in plaintext')
173
- t.is(htmlContent, '<div>Show in HTML</div>\n\n')
196
+ t.is(
197
+ await fs.readFile(`${t.context.folder}/front-matter.html`, 'utf8'),
198
+ '<div>Show in HTML</div>\n\n\n <table><tr><td>Remove from plaintext</td></tr></table>\n\n'
199
+ )
174
200
  })
175
201
 
176
202
  test('outputs plaintext files (custom path)', async t => {
@@ -187,18 +213,11 @@ test('outputs plaintext files (custom path)', async t => {
187
213
  path: `${t.context.folder}/nested/plain.text`
188
214
  }
189
215
  }
190
- },
191
- tailwind: {
192
- config: {
193
- purge: false
194
- }
195
216
  }
196
217
  }
197
218
  })
198
219
 
199
- const plaintext = files.filter(file => file.includes('.text'))
200
-
201
- t.is(plaintext[0], `${t.context.folder}/nested/plain.text`)
220
+ t.true(files.includes(`${t.context.folder}/nested/plain.text`))
202
221
  })
203
222
 
204
223
  test('renders plaintext string', async t => {
@@ -382,24 +401,6 @@ test('warns if a template cannot be rendered and `fail` option is `silent`', asy
382
401
  t.false(files.includes('empty.html'))
383
402
  })
384
403
 
385
- test('spins up local development server', async t => {
386
- await Maizzle.serve('local', {
387
- build: {
388
- browsersync: {
389
- ui: false
390
- },
391
- templates: {
392
- source: 'test/stubs/templates',
393
- destination: {
394
- path: t.context.folder
395
- }
396
- }
397
- }
398
- })
399
-
400
- t.true(await fs.pathExists(t.context.folder))
401
- })
402
-
403
404
  test('local server does not compile unwanted file types', async t => {
404
405
  await Maizzle.serve('local', {
405
406
  build: {
@@ -1,32 +1,18 @@
1
1
  const test = require('ava')
2
2
  const Maizzle = require('../src')
3
3
 
4
- const path = require('path')
5
- const fs = require('fs')
6
-
7
- const readFile = (dir, filename) => fs.promises
8
- .readFile(path.join(__dirname, dir, `${filename}.html`), 'utf8')
9
- .then(html => html.trim())
10
-
11
- const fixture = file => readFile('fixtures', file)
12
- const expected = file => readFile('expected', file)
13
-
14
4
  const renderString = (string, options = {}) => Maizzle.render(string, options).then(({html}) => html)
15
5
 
16
- test('compiles HTML string if no options are passed', async t => {
17
- const source = await fixture('basic')
18
-
19
- const html = await renderString(source)
20
-
21
- t.is(html, source)
22
- })
23
-
24
6
  test('uses environment config file(s) if available', async t => {
25
- const source = await fixture('useConfig')
7
+ const source = `<div>{{ page.mail }}</div>`
26
8
 
27
- const html = await renderString(source, {maizzle: {env: 'maizzle-ci'}})
9
+ const html = await renderString(source, {
10
+ maizzle: {
11
+ mail: 'puzzle'
12
+ }
13
+ })
28
14
 
29
- t.is(html, await expected('useConfig'))
15
+ t.is(html, '<div>puzzle</div>')
30
16
  })
31
17
 
32
18
  test('throws if first argument is not an HTML string', async t => {
@@ -129,7 +115,28 @@ test('prevents overwriting page object', async t => {
129
115
  })
130
116
 
131
117
  test('preserves css in marked style tags (tailwindcss)', async t => {
132
- const source = await fixture('transformers/preserve-transform-css')
118
+ const source = `<html>
119
+ <head>
120
+ <style tailwindcss preserve>
121
+ div {
122
+ @apply uppercase;
123
+ }
124
+ [data-ogsc] .inexistent {
125
+ color: #ef4444;
126
+ }
127
+ div > u + .body .gmail-android-block {
128
+ display: block !important;
129
+ }
130
+ u + #body a {
131
+ color: inherit;
132
+ }
133
+ </style>
134
+ </head>
135
+ <body>
136
+ <div>test</div>
137
+ </body>
138
+ </html>`
139
+
133
140
  const html = await renderString(source, {
134
141
  // So that we don't compile twice
135
142
  tailwind: {
@@ -137,12 +144,16 @@ test('preserves css in marked style tags (tailwindcss)', async t => {
137
144
  }
138
145
  })
139
146
 
140
- t.is(html, await expected('transformers/preserve-transform-css'))
147
+ t.true(html.includes('[data-ogsc] .inexistent'))
148
+ t.true(html.includes('div > u + .body .gmail-android-block'))
149
+ t.true(html.includes('u + #body a'))
141
150
  })
142
151
 
143
152
  test('@import css files in marked style tags', async t => {
144
- const source = await fixture('transformers/atimport-in-style')
153
+ const source = `<style postcss>@import "test/stubs/post.css";</style>`
145
154
  const html = await renderString(source)
146
155
 
147
- t.is(html, await expected('transformers/atimport-in-style'))
156
+ t.is(html, `<style>div {
157
+ margin: 1px 2px 3px 4px;
158
+ }</style>`)
148
159
  })
@@ -1,6 +1,5 @@
1
1
  const test = require('ava')
2
2
  const Maizzle = require('../src')
3
- const removePlaintextTags = require('../src/transformers/plaintext')
4
3
 
5
4
  const path = require('path')
6
5
  const fs = require('fs')
@@ -232,13 +231,6 @@ test('minify (disabled)', async t => {
232
231
  t.is(html, '<div>\n\n<p>\n\ntest</p></div>')
233
232
  })
234
233
 
235
- test('removes plaintext tag', t => {
236
- let html = removePlaintextTags('<plaintext>Removed</plaintext><div>Preserved</div>')
237
- html = html.replace(/[^\S\r\n]+$/gm, '').trim()
238
-
239
- t.is(html, '<div>Preserved</div>')
240
- })
241
-
242
234
  test('replace strings', async t => {
243
235
  const html = await Maizzle.replaceStrings('initial text', {initial: 'updated'})
244
236
 
@@ -295,48 +287,54 @@ test('filters (tailwindcss)', async t => {
295
287
  </style>`
296
288
  )
297
289
 
298
- const expected = `<style>.inline { display: inline !important
299
- } .table { display: table !important
300
- } .contents { display: contents !important
301
- } .hidden { display: none !important
302
- } .truncate { overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important
303
- } .uppercase { text-transform: uppercase !important
304
- } .lowercase { text-transform: lowercase !important
305
- } .capitalize { text-transform: capitalize !important
306
- } div { display: none
307
- }
308
- </style>`
309
-
310
- t.is(html, expected)
290
+ t.true(html.replace(/\s/g, '').includes(`div{display:none}`))
311
291
  })
312
292
 
313
293
  test('filters (postcss)', async t => {
314
294
  const html = await Maizzle.withFilters(
315
- `<style postcss>@import 'test/stubs/post.css';</style>`
295
+ `<style postcss>
296
+ div {
297
+ margin-top: 1px;
298
+ margin-right: 2px;
299
+ margin-bottom: 3px;
300
+ margin-left: 4px;
301
+ }
302
+ </style>`
316
303
  )
317
304
 
318
- const expected = `<style>div {
319
- margin: 1px 2px 3px 4px;
320
- }</style>`
321
-
322
- t.is(html, expected)
305
+ t.is(html.replace(/\n {2,}/g, ''), '<style>div {margin: 1px 2px 3px 4px;}</style>')
323
306
  })
324
307
 
325
308
  test('url parameters', async t => {
326
- const html = await Maizzle.addURLParams(
309
+ const simple = await Maizzle.addURLParams(
310
+ `<a href="https://example.com">test</a>
311
+ <link href="https://foo.bar">`,
312
+ {
313
+ bar: 'baz',
314
+ qix: 'qux'
315
+ }
316
+ )
317
+
318
+ const withOptions = await Maizzle.addURLParams(
327
319
  `<a href="example.com">test</a>
328
320
  <link href="https://foo.bar">`,
329
321
  {
330
322
  _options: {
331
323
  tags: ['a[href*="example"]'],
332
- strict: false
324
+ strict: false,
325
+ qs: {
326
+ encode: true
327
+ }
333
328
  },
334
- bar: 'baz',
335
- qix: 'qux'
329
+ foo: '@Bar@',
330
+ bar: 'baz'
336
331
  }
337
332
  )
338
333
 
339
- t.is(html, `<a href="example.com?bar=baz&qix=qux">test</a>
334
+ t.is(simple, `<a href="https://example.com?bar=baz&qix=qux">test</a>
335
+ <link href="https://foo.bar">`)
336
+
337
+ t.is(withOptions, `<a href="example.com?bar=baz&foo=%40Bar%40">test</a>
340
338
  <link href="https://foo.bar">`)
341
339
  })
342
340
 
@@ -1,23 +0,0 @@
1
- const {get} = require('lodash')
2
- const posthtml = require('posthtml')
3
-
4
- module.exports = (html, config = {}) => {
5
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
6
-
7
- return posthtml([plaintext()]).process(html, {...posthtmlOptions, sync: true}).html
8
- }
9
-
10
- const plaintext = () => tree => {
11
- const process = node => {
12
- if (node.tag === 'plaintext') {
13
- return {
14
- tag: false,
15
- content: ['']
16
- }
17
- }
18
-
19
- return node
20
- }
21
-
22
- return tree.walk(process)
23
- }
@@ -1,2 +0,0 @@
1
- Parent
2
- Child in second.html
@@ -1,3 +0,0 @@
1
- Environment: maizzle-ci
2
-
3
- Front matter variable: Hello
@@ -1,15 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <style>div {
5
- margin-top: 1px;
6
- margin-right: 2px;
7
- margin-bottom: 3px;
8
- margin-left: 4px;
9
- }
10
- </style>
11
- </head>
12
- <body>
13
- <div>test</div>
14
- </body>
15
- </html>
@@ -1,36 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <style data-embed="">.block {
5
- display: block !important;
6
- } .inline {
7
- display: inline !important;
8
- } .table {
9
- display: table !important;
10
- } .contents {
11
- display: contents !important;
12
- } .truncate {
13
- overflow: hidden !important;
14
- text-overflow: ellipsis !important;
15
- white-space: nowrap !important;
16
- } .uppercase {
17
- text-transform: uppercase !important;
18
- } .lowercase {
19
- text-transform: lowercase !important;
20
- } .capitalize {
21
- text-transform: capitalize !important;
22
- } div {
23
- text-transform: uppercase;
24
- } [data-ogsc] .inexistent {
25
- color: #ef4444;
26
- } div > u + .body .gmail-android-block {
27
- display: block !important;
28
- } u + #body a {
29
- color: inherit;
30
- }
31
- </style>
32
- </head>
33
- <body>
34
- <div>test</div>
35
- </body>
36
- </html>
@@ -1,9 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <title>test></title>
5
- </head>
6
- <body>
7
- <div>build_production</div>
8
- </body>
9
- </html>
@@ -1,7 +0,0 @@
1
- ---
2
- template: second
3
- ---
4
-
5
- <extends src="test/stubs/template.html">
6
- <block name="button">Child in second.html</block>
7
- </extends>
@@ -1,11 +0,0 @@
1
- ---
2
- greeting: Hello
3
- ---
4
-
5
- <extends src="test/stubs/layouts/basic.html">
6
- <block name="template">
7
- Environment: {{ page.env }}
8
-
9
- Front matter variable: {{ page.greeting }}
10
- </block>
11
- </extends>
@@ -1,11 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <style postcss>
5
- @import "test/stubs/post.css";
6
- </style>
7
- </head>
8
- <body>
9
- <div>test</div>
10
- </body>
11
- </html>
@@ -1,25 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <style tailwindcss preserve>
5
- div {
6
- @apply uppercase;
7
- }
8
-
9
- [data-ogsc] .inexistent {
10
- color: #ef4444;
11
- }
12
-
13
- div > u + .body .gmail-android-block {
14
- display: block !important;
15
- }
16
-
17
- u + #body a {
18
- color: inherit;
19
- }
20
- </style>
21
- </head>
22
- <body>
23
- <div>test</div>
24
- </body>
25
- </html>
@@ -1,9 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <title>test></title>
5
- </head>
6
- <body>
7
- <div>{{ page.build.templates.destination.path }}</div>
8
- </body>
9
- </html>