@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 +2 -2
- package/src/generators/output/to-disk.js +10 -8
- package/src/generators/output/to-string.js +3 -1
- package/src/generators/plaintext.js +69 -34
- package/src/generators/postcss.js +3 -11
- package/src/generators/posthtml.js +1 -0
- package/src/generators/tailwindcss.js +14 -15
- package/src/transformers/attributeToStyle.js +11 -11
- package/src/transformers/posthtmlMso.js +1 -1
- package/src/transformers/urlParameters.js +4 -4
- package/test/stubs/plaintext/front-matter.html +9 -0
- package/test/stubs/plaintext/plaintext.html +3 -0
- package/test/test-postcss.js +1 -1
- package/test/test-posthtml.js +59 -8
- package/test/test-tailwindcss.js +2 -4
- package/test/test-todisk.js +40 -39
- package/test/test-tostring.js +36 -25
- package/test/test-transformers.js +30 -32
- package/src/transformers/plaintext.js +0 -23
- package/test/expected/posthtml/extend-template.html +0 -2
- package/test/expected/posthtml/layout.html +0 -3
- package/test/expected/transformers/atimport-in-style.html +0 -15
- package/test/expected/transformers/preserve-transform-css.html +0 -36
- package/test/expected/useConfig.html +0 -9
- package/test/fixtures/posthtml/extend-template.html +0 -7
- package/test/fixtures/posthtml/layout.html +0 -11
- package/test/fixtures/transformers/atimport-in-style.html +0 -11
- package/test/fixtures/transformers/preserve-transform-css.html +0 -25
- package/test/fixtures/useConfig.html +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maizzle/framework",
|
|
3
|
-
"version": "4.1
|
|
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
|
|
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
|
|
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
|
-
|
|
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}) =>
|
|
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 =
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const self = {
|
|
7
|
+
handleCustomTags: (html, config = {}) => {
|
|
8
|
+
const posthtmlOptions = get(config, 'build.posthtml.options', {})
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
71
|
+
* Use template's `permalink` Front Matter key,
|
|
72
|
+
* fall back to the original `destination`.
|
|
32
73
|
*/
|
|
33
|
-
destination =
|
|
34
|
-
|
|
35
|
-
return {plaintext, destination}
|
|
36
|
-
}
|
|
74
|
+
destination = get(config, 'permalink', destination)
|
|
37
75
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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 = {}
|
|
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(
|
|
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
|
-
|
|
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
|
}
|
|
@@ -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 = {}
|
|
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 = `@
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
42
|
+
cssToInline.push(`background-color: ${value}`)
|
|
43
43
|
break
|
|
44
44
|
|
|
45
45
|
case 'background':
|
|
46
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
+
cssToInline.push(`height: ${value}`)
|
|
57
57
|
break
|
|
58
58
|
|
|
59
59
|
case 'align':
|
|
60
60
|
if (node.tag !== 'table') {
|
|
61
|
-
return
|
|
61
|
+
return cssToInline.push(`text-align: ${value}`)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
if (['left', 'right'].includes(value)) {
|
|
65
|
-
|
|
65
|
+
cssToInline.push(`float: ${value}`)
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
if (value === 'center') {
|
|
69
|
-
|
|
69
|
+
cssToInline.push('margin-left: auto', 'margin-right: auto')
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
break
|
|
73
73
|
|
|
74
74
|
case 'valign':
|
|
75
|
-
|
|
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} ${
|
|
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,
|
|
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
|
}
|
package/test/test-postcss.js
CHANGED
|
@@ -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:
|
|
7
|
+
}, {instanceOf: SyntaxError})
|
|
8
8
|
})
|
package/test/test-posthtml.js
CHANGED
|
@@ -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 =
|
|
17
|
+
const source = `---
|
|
18
|
+
greeting: Hello
|
|
19
|
+
---
|
|
18
20
|
|
|
19
|
-
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
49
|
+
t.is(html, `Parent
|
|
50
|
+
Child in second.html`)
|
|
31
51
|
})
|
|
32
52
|
|
|
33
53
|
test('components', async t => {
|
|
34
|
-
const source =
|
|
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: '
|
|
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(),
|
|
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 => {
|
package/test/test-tailwindcss.js
CHANGED
|
@@ -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
|
-
|
|
8
|
-
|
|
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 => {
|
package/test/test-todisk.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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(
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
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: {
|
package/test/test-tostring.js
CHANGED
|
@@ -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 =
|
|
7
|
+
const source = `<div>{{ page.mail }}</div>`
|
|
26
8
|
|
|
27
|
-
const html = await renderString(source, {
|
|
9
|
+
const html = await renderString(source, {
|
|
10
|
+
maizzle: {
|
|
11
|
+
mail: 'puzzle'
|
|
12
|
+
}
|
|
13
|
+
})
|
|
28
14
|
|
|
29
|
-
t.is(html,
|
|
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 =
|
|
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.
|
|
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 =
|
|
153
|
+
const source = `<style postcss>@import "test/stubs/post.css";</style>`
|
|
145
154
|
const html = await renderString(source)
|
|
146
155
|
|
|
147
|
-
t.is(html,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
335
|
-
|
|
329
|
+
foo: '@Bar@',
|
|
330
|
+
bar: 'baz'
|
|
336
331
|
}
|
|
337
332
|
)
|
|
338
333
|
|
|
339
|
-
t.is(
|
|
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,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,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>
|