@maizzle/framework 3.7.3 → 4.0.0-alpha.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.
- package/.github/workflows/nodejs.yml +0 -1
- package/package.json +15 -13
- package/src/commands/build.js +32 -0
- package/src/commands/serve.js +139 -0
- package/src/functions/plaintext.js +5 -0
- package/src/functions/render.js +5 -0
- package/src/generators/output/to-disk.js +208 -167
- package/src/generators/output/to-string.js +5 -2
- package/src/generators/plaintext.js +49 -52
- package/src/generators/posthtml.js +61 -60
- package/src/generators/tailwindcss.js +114 -84
- package/src/index.js +13 -163
- package/src/transformers/{attribute-to-style.js → attributeToStyle.js} +0 -0
- package/src/transformers/baseUrl.js +45 -0
- package/src/transformers/{extra-attributes.js → extraAttributes.js} +0 -0
- package/src/transformers/index.js +57 -57
- package/src/transformers/{inline.js → inlineCss.js} +0 -0
- package/src/transformers/{posthtml-mso.js → posthtmlMso.js} +0 -0
- package/src/transformers/{prevent-widows.js → preventWidows.js} +0 -0
- package/src/transformers/{remove-attributes.js → removeAttributes.js} +1 -1
- package/src/transformers/{remove-inline-bgcolor.js → removeInlineBackgroundColor.js} +0 -0
- package/src/transformers/{remove-inline-sizes.js → removeInlineSizes.js} +0 -0
- package/src/transformers/{remove-unused-css.js → removeUnusedCss.js} +0 -0
- package/src/transformers/{replace-strings.js → replaceStrings.js} +0 -0
- package/src/transformers/{safe-class-names.js → safeClassNames.js} +8 -2
- package/src/transformers/{six-hex.js → sixHex.js} +0 -0
- package/src/transformers/transform.js +4 -6
- package/src/transformers/{url-params.js → urlParameters.js} +0 -0
- package/src/utils/helpers.js +2 -8
- package/test/expected/transformers/base-image-url.html +82 -6
- package/test/expected/transformers/transform-postcss.html +19 -0
- package/test/expected/useConfig.html +9 -0
- package/test/fixtures/basic.html +9 -9
- package/test/fixtures/transformers/base-image-url.html +84 -6
- package/test/fixtures/useConfig.html +9 -0
- package/test/stubs/tailwind/preserve.html +1 -0
- package/test/test-tailwind.js +68 -24
- package/test/test-todisk.js +497 -418
- package/test/test-tostring.js +132 -124
- package/test/test-transformers.js +25 -2
- package/xo.config.js +3 -0
- package/src/transformers/base-image-url.js +0 -9
|
@@ -1,52 +1,49 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
const {get} = require('lodash')
|
|
3
|
-
const {stripHtml} = require('string-strip-html')
|
|
4
|
-
|
|
5
|
-
module.exports.generate = async (html, destination, config) => {
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const plaintext = stripHtml(html, {
|
|
10
|
-
dumpLinkHrefsNearby: {
|
|
11
|
-
enabled: true
|
|
12
|
-
},
|
|
13
|
-
...options
|
|
14
|
-
}).result
|
|
15
|
-
|
|
16
|
-
// If we set plaintext.destination.path in config/fm
|
|
17
|
-
if (configDestinationPath) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
*
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return {plaintext, destination}
|
|
52
|
-
}
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const {get} = require('lodash')
|
|
3
|
+
const {stripHtml} = require('string-strip-html')
|
|
4
|
+
|
|
5
|
+
module.exports.generate = async (html, destination, config) => {
|
|
6
|
+
const configDestinationPath = get(config, 'destination.path')
|
|
7
|
+
const extension = get(config, 'destination.extension', 'txt')
|
|
8
|
+
|
|
9
|
+
const plaintext = stripHtml(html, {
|
|
10
|
+
dumpLinkHrefsNearby: {
|
|
11
|
+
enabled: true
|
|
12
|
+
},
|
|
13
|
+
...get(config, 'options', {})
|
|
14
|
+
}).result
|
|
15
|
+
|
|
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
|
|
26
|
+
|
|
27
|
+
return {plaintext, destination}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Using a directory-like path for plaintext.destination.path
|
|
32
|
+
*/
|
|
33
|
+
destination = path.join(configDestinationPath, path.basename(config.filepath, path.extname(config.filepath)) + '.' + extension)
|
|
34
|
+
|
|
35
|
+
return {plaintext, destination}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Use template's `permalink` Front Matter key,
|
|
40
|
+
* fall back to the original `destination`.
|
|
41
|
+
*/
|
|
42
|
+
destination = get(config, 'permalink', destination)
|
|
43
|
+
|
|
44
|
+
if (typeof destination === 'string') {
|
|
45
|
+
destination = path.join(path.dirname(destination), path.basename(destination, path.extname(destination)) + '.' + extension)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {plaintext, destination}
|
|
49
|
+
}
|
|
@@ -1,60 +1,61 @@
|
|
|
1
|
-
const fm = require('front-matter')
|
|
2
|
-
const posthtml = require('posthtml')
|
|
3
|
-
const {get, merge} = require('lodash')
|
|
4
|
-
const fetch = require('posthtml-fetch')
|
|
5
|
-
const layouts = require('posthtml-extend')
|
|
6
|
-
const modules = require('posthtml-modules')
|
|
7
|
-
const expressions = require('posthtml-expressions')
|
|
8
|
-
|
|
9
|
-
module.exports = async (html, config) => {
|
|
10
|
-
const layoutsOptions = get(config, 'build.layouts', {})
|
|
11
|
-
|
|
12
|
-
const fetchOptions = get(config, 'build.posthtml.fetch', {})
|
|
13
|
-
const fetchPlugin = fetch({...fetchOptions})
|
|
14
|
-
|
|
15
|
-
const modulesOptions = get(config, 'build.components', {})
|
|
16
|
-
// Fake `from` option so we can reference modules relatively
|
|
17
|
-
const modulesRoot = modulesOptions.root || './'
|
|
18
|
-
const modulesFrom = modulesOptions.from || `${modulesRoot}/fake`
|
|
19
|
-
|
|
20
|
-
const posthtmlOptions = get(config, 'build.posthtml.options', {})
|
|
21
|
-
const posthtmlPlugins = get(config, 'build.posthtml.plugins', [])
|
|
22
|
-
|
|
23
|
-
const expressionsOptions = merge({strictMode: false}, get(config, 'build.posthtml.expressions', {}))
|
|
24
|
-
|
|
25
|
-
const locals = merge(
|
|
26
|
-
get(expressionsOptions, 'locals', {}),
|
|
27
|
-
get(config, 'locals', {}),
|
|
28
|
-
{page: config}
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
return posthtml([
|
|
32
|
-
fetchPlugin,
|
|
33
|
-
layouts(
|
|
34
|
-
merge(
|
|
35
|
-
{
|
|
36
|
-
strict: false,
|
|
37
|
-
plugins: [
|
|
38
|
-
expressions({...expressionsOptions, locals})
|
|
39
|
-
]
|
|
40
|
-
},
|
|
41
|
-
layoutsOptions
|
|
42
|
-
)
|
|
43
|
-
),
|
|
44
|
-
modules({
|
|
45
|
-
parser: posthtmlOptions,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
.
|
|
60
|
-
|
|
1
|
+
const fm = require('front-matter')
|
|
2
|
+
const posthtml = require('posthtml')
|
|
3
|
+
const {get, merge} = require('lodash')
|
|
4
|
+
const fetch = require('posthtml-fetch')
|
|
5
|
+
const layouts = require('posthtml-extend')
|
|
6
|
+
const modules = require('posthtml-modules')
|
|
7
|
+
const expressions = require('posthtml-expressions')
|
|
8
|
+
|
|
9
|
+
module.exports = async (html, config) => {
|
|
10
|
+
const layoutsOptions = get(config, 'build.layouts', {})
|
|
11
|
+
|
|
12
|
+
const fetchOptions = get(config, 'build.posthtml.fetch', {})
|
|
13
|
+
const fetchPlugin = fetch({...fetchOptions})
|
|
14
|
+
|
|
15
|
+
const modulesOptions = get(config, 'build.components', {})
|
|
16
|
+
// Fake `from` option so we can reference modules relatively
|
|
17
|
+
const modulesRoot = modulesOptions.root || './'
|
|
18
|
+
const modulesFrom = modulesOptions.from || `${modulesRoot}/fake`
|
|
19
|
+
|
|
20
|
+
const posthtmlOptions = get(config, 'build.posthtml.options', {})
|
|
21
|
+
const posthtmlPlugins = get(config, 'build.posthtml.plugins', [])
|
|
22
|
+
|
|
23
|
+
const expressionsOptions = merge({strictMode: false}, get(config, 'build.posthtml.expressions', {}))
|
|
24
|
+
|
|
25
|
+
const locals = merge(
|
|
26
|
+
get(expressionsOptions, 'locals', {}),
|
|
27
|
+
get(config, 'locals', {}),
|
|
28
|
+
{page: config}
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
return posthtml([
|
|
32
|
+
fetchPlugin,
|
|
33
|
+
layouts(
|
|
34
|
+
merge(
|
|
35
|
+
{
|
|
36
|
+
strict: false,
|
|
37
|
+
plugins: [
|
|
38
|
+
expressions({...expressionsOptions, locals})
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
layoutsOptions
|
|
42
|
+
)
|
|
43
|
+
),
|
|
44
|
+
modules({
|
|
45
|
+
parser: posthtmlOptions,
|
|
46
|
+
attributeAsLocals: true,
|
|
47
|
+
from: modulesFrom,
|
|
48
|
+
root: modulesRoot,
|
|
49
|
+
tag: 'component',
|
|
50
|
+
attribute: 'src',
|
|
51
|
+
plugins: [
|
|
52
|
+
fetchPlugin
|
|
53
|
+
],
|
|
54
|
+
locals,
|
|
55
|
+
...modulesOptions
|
|
56
|
+
}),
|
|
57
|
+
...posthtmlPlugins
|
|
58
|
+
])
|
|
59
|
+
.process(html, {...posthtmlOptions})
|
|
60
|
+
.then(result => fm(result.html).body)
|
|
61
|
+
}
|
|
@@ -1,84 +1,114 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
const fs = require('fs-extra')
|
|
3
|
-
const postcss = require('postcss')
|
|
4
|
-
const tailwindcss = require('tailwindcss')
|
|
5
|
-
const postcssImport = require('postcss-import')
|
|
6
|
-
const postcssNested = require('tailwindcss/nesting')
|
|
7
|
-
const {requireUncached} = require('../utils/helpers')
|
|
8
|
-
const mergeLonghand = require('postcss-merge-longhand')
|
|
9
|
-
const {get, isObject, isEmpty, merge} = require('lodash')
|
|
10
|
-
|
|
11
|
-
module.exports = {
|
|
12
|
-
compile: async (css = '', html = '', tailwindConfig = {}, maizzleConfig = {}) => {
|
|
13
|
-
tailwindConfig = (isObject(tailwindConfig) && !isEmpty(tailwindConfig)) ? tailwindConfig : get(maizzleConfig, 'build.tailwind.config', 'tailwind.config.js')
|
|
14
|
-
|
|
15
|
-
// Compute the Tailwind config to use
|
|
16
|
-
const userConfig = () => {
|
|
17
|
-
// If a custom config object was passed, use that
|
|
18
|
-
if (isObject(tailwindConfig) && !isEmpty(tailwindConfig)) {
|
|
19
|
-
return tailwindConfig
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Try loading a fresh tailwind.config.js, with fallback to an empty object.
|
|
24
|
-
* This will use the default Tailwind config (with rem units etc)
|
|
25
|
-
*/
|
|
26
|
-
try {
|
|
27
|
-
return requireUncached(path.resolve(process.cwd(), tailwindConfig))
|
|
28
|
-
} catch {
|
|
29
|
-
return {}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Merge user's Tailwind config on top of a 'base' config
|
|
34
|
-
const config = merge({
|
|
35
|
-
important: true,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
'
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const fs = require('fs-extra')
|
|
3
|
+
const postcss = require('postcss')
|
|
4
|
+
const tailwindcss = require('tailwindcss')
|
|
5
|
+
const postcssImport = require('postcss-import')
|
|
6
|
+
const postcssNested = require('tailwindcss/nesting')
|
|
7
|
+
const {requireUncached} = require('../utils/helpers')
|
|
8
|
+
const mergeLonghand = require('postcss-merge-longhand')
|
|
9
|
+
const {get, isObject, isEmpty, merge} = require('lodash')
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
compile: async (css = '', html = '', tailwindConfig = {}, maizzleConfig = {}, spinner = null) => {
|
|
13
|
+
tailwindConfig = (isObject(tailwindConfig) && !isEmpty(tailwindConfig)) ? tailwindConfig : get(maizzleConfig, 'build.tailwind.config', 'tailwind.config.js')
|
|
14
|
+
|
|
15
|
+
// Compute the Tailwind config to use
|
|
16
|
+
const userConfig = () => {
|
|
17
|
+
// If a custom config object was passed, use that
|
|
18
|
+
if (isObject(tailwindConfig) && !isEmpty(tailwindConfig)) {
|
|
19
|
+
return tailwindConfig
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Try loading a fresh tailwind.config.js, with fallback to an empty object.
|
|
24
|
+
* This will use the default Tailwind config (with rem units etc)
|
|
25
|
+
*/
|
|
26
|
+
try {
|
|
27
|
+
return requireUncached(path.resolve(process.cwd(), tailwindConfig))
|
|
28
|
+
} catch {
|
|
29
|
+
return {}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Merge user's Tailwind config on top of a 'base' config
|
|
34
|
+
const config = merge({
|
|
35
|
+
important: true,
|
|
36
|
+
content: {
|
|
37
|
+
files: [
|
|
38
|
+
'./src/**/*.*',
|
|
39
|
+
{raw: html, extension: 'html'}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}, userConfig())
|
|
43
|
+
|
|
44
|
+
// Add back the `{raw: html}` option if user provided own config
|
|
45
|
+
if (Array.isArray(config.content)) {
|
|
46
|
+
config.content = {
|
|
47
|
+
files: [
|
|
48
|
+
...config.content,
|
|
49
|
+
'./src/**/*.*',
|
|
50
|
+
{raw: html, extension: 'html'}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Include all `build.templates.source` paths when scanning for selectors to preserve
|
|
56
|
+
const buildTemplates = get(maizzleConfig, 'build.templates')
|
|
57
|
+
|
|
58
|
+
if (buildTemplates) {
|
|
59
|
+
const templateObjects = Array.isArray(buildTemplates) ? buildTemplates : [buildTemplates]
|
|
60
|
+
const templateSources = templateObjects.map(template => {
|
|
61
|
+
const source = get(template, 'source')
|
|
62
|
+
|
|
63
|
+
if (typeof source === 'function') {
|
|
64
|
+
const sources = source(maizzleConfig)
|
|
65
|
+
|
|
66
|
+
if (Array.isArray(sources)) {
|
|
67
|
+
sources.map(s => config.content.files.push(s))
|
|
68
|
+
} else if (typeof sources === 'string') {
|
|
69
|
+
config.content.files.push(sources)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Must return a valid `content` entry
|
|
73
|
+
return {raw: '', extension: 'html'}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Support single-file sources i.e. src/templates/index.html
|
|
77
|
+
if (typeof source === 'string' && Boolean(path.extname(source))) {
|
|
78
|
+
config.content.files.push(source)
|
|
79
|
+
|
|
80
|
+
return {raw: '', extension: 'html'}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return `${source}/**/*.*`
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
config.content.files.push(...templateSources)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const userFilePath = get(maizzleConfig, 'build.tailwind.css', path.join(process.cwd(), 'src/css/tailwind.css'))
|
|
90
|
+
const userFileExists = await fs.pathExists(userFilePath)
|
|
91
|
+
|
|
92
|
+
if (userFileExists) {
|
|
93
|
+
css = await fs.readFile(path.resolve(userFilePath), 'utf8') + css
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return postcss([
|
|
97
|
+
postcssImport({path: path.dirname(userFilePath)}),
|
|
98
|
+
postcssNested(),
|
|
99
|
+
tailwindcss(config),
|
|
100
|
+
maizzleConfig.env === 'local' ? () => {} : mergeLonghand(),
|
|
101
|
+
...get(maizzleConfig, 'build.postcss.plugins', [])
|
|
102
|
+
])
|
|
103
|
+
.process(css, {from: undefined})
|
|
104
|
+
.then(result => result.css)
|
|
105
|
+
.catch(error => {
|
|
106
|
+
console.error(error)
|
|
107
|
+
if (spinner) {
|
|
108
|
+
spinner.stop()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
throw new Error(`Tailwind CSS compilation failed`)
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,163 +1,13 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
...transformers,
|
|
15
|
-
render: async (html, options) => Output.toString(html, options),
|
|
16
|
-
plaintext: async (html, config = {}) => Plaintext.generate(html, false, config),
|
|
17
|
-
build: async (env = 'local', config = {}) => {
|
|
18
|
-
const start = new Date()
|
|
19
|
-
const spinner = ora('Building emails...').start()
|
|
20
|
-
|
|
21
|
-
return Output.toDisk(env, spinner, config)
|
|
22
|
-
.then(({files, parsed}) => {
|
|
23
|
-
const elapsedSeconds = (Date.now() - start) / 1000
|
|
24
|
-
|
|
25
|
-
if (get(config, 'build.command') === 'serve') {
|
|
26
|
-
clearConsole()
|
|
27
|
-
spinner.succeed(`Re-built ${parsed.length} templates in ${elapsedSeconds}s`)
|
|
28
|
-
} else {
|
|
29
|
-
spinner.succeed(`Built ${parsed.length} templates in ${elapsedSeconds}s`)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return {files}
|
|
33
|
-
})
|
|
34
|
-
.catch(error => {
|
|
35
|
-
throw error
|
|
36
|
-
})
|
|
37
|
-
},
|
|
38
|
-
serve: async (env = 'local', config = {}) => {
|
|
39
|
-
config = merge(
|
|
40
|
-
config,
|
|
41
|
-
await Config.getMerged(env),
|
|
42
|
-
{
|
|
43
|
-
build: {
|
|
44
|
-
command: 'serve'
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
await self
|
|
50
|
-
.build(env, config)
|
|
51
|
-
.then(async () => {
|
|
52
|
-
let templates = get(config, 'build.templates')
|
|
53
|
-
templates = Array.isArray(templates) ? templates : [templates]
|
|
54
|
-
|
|
55
|
-
const templatePaths = [...new Set(templates.map(config => `${get(config, 'source', 'src')}/**`))]
|
|
56
|
-
const globalPaths = [
|
|
57
|
-
'src/**',
|
|
58
|
-
get(config, 'build.tailwind.config', 'tailwind.config.js'),
|
|
59
|
-
[...new Set(get(config, 'build.browsersync.watch', []))]
|
|
60
|
-
]
|
|
61
|
-
|
|
62
|
-
// Pre-compile Tailwind so that updates to tailwind.config.js are reflected
|
|
63
|
-
const cssString = fs.existsSync(get(config, 'build.tailwind.css')) ? fs.readFileSync(get(config, 'build.tailwind.css'), 'utf8') : '@tailwind components; @tailwind utilities;'
|
|
64
|
-
const css = await Tailwind.compile(cssString, '', {}, config)
|
|
65
|
-
|
|
66
|
-
const spinner = ora()
|
|
67
|
-
|
|
68
|
-
// Watch for Template file changes
|
|
69
|
-
bs.watch(templatePaths)
|
|
70
|
-
.on('change', async file => {
|
|
71
|
-
clearConsole()
|
|
72
|
-
|
|
73
|
-
const start = new Date()
|
|
74
|
-
|
|
75
|
-
spinner.start('Building email...')
|
|
76
|
-
|
|
77
|
-
file = file.replace(/\\/g, '/')
|
|
78
|
-
|
|
79
|
-
const destination = get(config, 'build.currentTemplates.destination.path', 'build_local')
|
|
80
|
-
const extension = get(config, 'build.currentTemplates.destination.extension', 'html')
|
|
81
|
-
const fileSource = get(config, 'build.currentTemplates.source')
|
|
82
|
-
const parts = path.parse(path.join(destination, file.replace(fileSource, '')))
|
|
83
|
-
const finalDestination = path.join(parts.dir, `${parts.name}.${extension}`)
|
|
84
|
-
|
|
85
|
-
if (config.events && typeof config.events.beforeCreate === 'function') {
|
|
86
|
-
await config.events.beforeCreate(config)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Tailwind CSS compiler
|
|
91
|
-
*
|
|
92
|
-
* Use the Just-In-Time engine if the user enabled it
|
|
93
|
-
* Fall back to the classic Always-On-Time engine
|
|
94
|
-
*/
|
|
95
|
-
let mode = 'aot'
|
|
96
|
-
|
|
97
|
-
try {
|
|
98
|
-
const tailwindConfig = require(path.resolve(process.cwd(), get(config, 'build.currentTemplates.tailwind.config', 'tailwind.config.js')))
|
|
99
|
-
mode = get(tailwindConfig, 'mode')
|
|
100
|
-
} catch {}
|
|
101
|
-
|
|
102
|
-
const renderOptions = {
|
|
103
|
-
maizzle: config,
|
|
104
|
-
...config.events
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// AOT: fall back to pre-compiled CSS
|
|
108
|
-
if (mode !== 'jit') {
|
|
109
|
-
renderOptions.tailwind = {
|
|
110
|
-
compiled: css
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
self
|
|
115
|
-
.render(await fs.readFile(file, 'utf8'), renderOptions)
|
|
116
|
-
.then(({html, config}) => fs.outputFile(config.permalink || finalDestination, html))
|
|
117
|
-
.then(() => {
|
|
118
|
-
bs.reload()
|
|
119
|
-
spinner.succeed(`Compiled in ${(Date.now() - start) / 1000}s [${file}]`)
|
|
120
|
-
})
|
|
121
|
-
.catch(() => spinner.warn(`Received empty HTML, please save your file again [${file}]`))
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
// Watch for changes in all other files
|
|
125
|
-
bs.watch(globalPaths, {ignored: templatePaths})
|
|
126
|
-
.on('change', () => self.build(env, config).then(() => bs.reload()))
|
|
127
|
-
.on('unlink', () => self.build(env, config).then(() => bs.reload()))
|
|
128
|
-
|
|
129
|
-
// Watch for changes in config files
|
|
130
|
-
bs.watch('config*.js')
|
|
131
|
-
.on('change', async file => {
|
|
132
|
-
const parsedEnv = path.parse(file).name.split('.')[1] || 'local'
|
|
133
|
-
|
|
134
|
-
Config
|
|
135
|
-
.getMerged(parsedEnv)
|
|
136
|
-
.then(config => self.build(parsedEnv, config).then(() => bs.reload()))
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
// Browsersync options
|
|
140
|
-
const baseDir = templates.map(t => t.destination.path)
|
|
141
|
-
|
|
142
|
-
const bsOptions = {
|
|
143
|
-
notify: false,
|
|
144
|
-
open: false,
|
|
145
|
-
port: 3000,
|
|
146
|
-
server: {
|
|
147
|
-
baseDir,
|
|
148
|
-
directory: true
|
|
149
|
-
},
|
|
150
|
-
tunnel: false,
|
|
151
|
-
ui: {port: 3001},
|
|
152
|
-
logFileChanges: false,
|
|
153
|
-
...get(config, 'build.browsersync', {})
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Initialize Browsersync
|
|
157
|
-
bs.init(bsOptions, () => {})
|
|
158
|
-
})
|
|
159
|
-
.catch(error => {
|
|
160
|
-
throw error
|
|
161
|
-
})
|
|
162
|
-
}
|
|
163
|
-
}
|
|
1
|
+
const serve = require('./commands/serve')
|
|
2
|
+
const toFile = require('./commands/build')
|
|
3
|
+
const transformers = require('./transformers')
|
|
4
|
+
const toString = require('./functions/render')
|
|
5
|
+
const toPlaintext = require('./functions/plaintext')
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
serve,
|
|
9
|
+
build: toFile,
|
|
10
|
+
...transformers,
|
|
11
|
+
render: toString,
|
|
12
|
+
plaintext: toPlaintext
|
|
13
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const posthtml = require('posthtml')
|
|
2
|
+
const isUrl = require('is-url-superb')
|
|
3
|
+
const baseUrl = require('posthtml-base-url')
|
|
4
|
+
const {get, isObject, isEmpty} = require('lodash')
|
|
5
|
+
|
|
6
|
+
// VML backgrounds need regex because they're inside HTML comments :(
|
|
7
|
+
const rewriteVMLs = (html, url) => {
|
|
8
|
+
const vImageMatch = html.match(/(<v:image.+)(src=['"]([^'"]+)['"])/)
|
|
9
|
+
const vFillMatch = html.match(/(<v:fill.+)(src=['"]([^'"]+)['"])/)
|
|
10
|
+
|
|
11
|
+
if (vImageMatch && !isUrl(vImageMatch[3])) {
|
|
12
|
+
html = html.replace(vImageMatch[0], `${vImageMatch[1]}src="${url}${vImageMatch[3]}"`)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (vFillMatch && !isUrl(vFillMatch[3])) {
|
|
16
|
+
html = html.replace(vFillMatch[0], `${vFillMatch[1]}src="${url}${vFillMatch[3]}"`)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return html
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = async (html, config = {}, direct = false) => {
|
|
23
|
+
const url = direct ? config : get(config, 'baseURL')
|
|
24
|
+
const posthtmlOptions = get(config, 'build.posthtml.options', {})
|
|
25
|
+
|
|
26
|
+
// `baseUrl` as a string
|
|
27
|
+
if (typeof url === 'string' && url.length > 0) {
|
|
28
|
+
html = rewriteVMLs(html, url)
|
|
29
|
+
|
|
30
|
+
return posthtml([
|
|
31
|
+
baseUrl({url, allTags: true, styleTag: true, inlineCss: true})
|
|
32
|
+
])
|
|
33
|
+
.process(html, posthtmlOptions)
|
|
34
|
+
.then(result => result.html)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// `baseUrl: {}`
|
|
38
|
+
if (isObject(url) && !isEmpty(url)) {
|
|
39
|
+
html = rewriteVMLs(html, url.url)
|
|
40
|
+
|
|
41
|
+
return posthtml([baseUrl(url)]).process(html, posthtmlOptions).then(result => result.html)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return html
|
|
45
|
+
}
|
|
File without changes
|