@maizzle/framework 4.0.0 → 4.0.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.
Files changed (58) hide show
  1. package/.editorconfig +9 -9
  2. package/.github/workflows/nodejs.yml +28 -28
  3. package/LICENSE +21 -21
  4. package/bin/maizzle +3 -3
  5. package/package.json +2 -2
  6. package/src/generators/config.js +32 -32
  7. package/src/generators/output/index.js +4 -4
  8. package/src/generators/output/to-disk.js +208 -208
  9. package/src/generators/output/to-string.js +67 -67
  10. package/src/generators/postcss.js +29 -29
  11. package/src/generators/posthtml.js +66 -66
  12. package/src/index.js +17 -17
  13. package/src/transformers/attributeToStyle.js +90 -90
  14. package/src/transformers/baseUrl.js +69 -69
  15. package/src/transformers/extraAttributes.js +26 -26
  16. package/src/transformers/filters/defaultFilters.js +126 -126
  17. package/src/transformers/filters/index.js +55 -55
  18. package/src/transformers/inlineCss.js +37 -37
  19. package/src/transformers/markdown.js +19 -19
  20. package/src/transformers/minify.js +21 -21
  21. package/src/transformers/plaintext.js +23 -23
  22. package/src/transformers/posthtmlMso.js +10 -10
  23. package/src/transformers/prettify.js +27 -27
  24. package/src/transformers/preventWidows.js +13 -13
  25. package/src/transformers/removeAttributes.js +17 -17
  26. package/src/transformers/removeInlineBackgroundColor.js +52 -52
  27. package/src/transformers/removeInlineSizes.js +41 -41
  28. package/src/transformers/replaceStrings.js +14 -14
  29. package/src/transformers/safeClassNames.js +24 -24
  30. package/src/transformers/shorthandInlineCSS.js +19 -19
  31. package/src/transformers/sixHex.js +33 -33
  32. package/src/transformers/urlParameters.js +17 -17
  33. package/src/utils/helpers.js +17 -17
  34. package/test/expected/posthtml/extend-template.html +2 -2
  35. package/test/expected/posthtml/fetch.html +5 -5
  36. package/test/expected/posthtml/layout.html +3 -3
  37. package/test/expected/transformers/base-url.html +99 -99
  38. package/test/fixtures/posthtml/extend-template.html +7 -7
  39. package/test/fixtures/posthtml/fetch.html +9 -9
  40. package/test/fixtures/posthtml/layout.html +11 -11
  41. package/test/fixtures/transformers/base-url.html +101 -101
  42. package/test/stubs/assets/foo.bar +1 -1
  43. package/test/stubs/breaking/bad.html +5 -5
  44. package/test/stubs/config/config.js +10 -10
  45. package/test/stubs/config/config.maizzle-ci.js +10 -10
  46. package/test/stubs/data.json +14 -14
  47. package/test/stubs/events/before-create.html +1 -1
  48. package/test/stubs/layouts/basic.html +1 -1
  49. package/test/stubs/layouts/full.html +12 -12
  50. package/test/stubs/layouts/template.html +5 -5
  51. package/test/stubs/main.css +5 -5
  52. package/test/stubs/tailwind/content-source.html +1 -1
  53. package/test/stubs/tailwind/tailwind.css +3 -3
  54. package/test/stubs/template.html +10 -10
  55. package/test/test-posthtml.js +72 -72
  56. package/test/test-todisk.js +511 -511
  57. package/test/test-transformers.js +1 -0
  58. package/xo.config.js +22 -22
package/.editorconfig CHANGED
@@ -1,9 +1,9 @@
1
- root = true
2
-
3
- [*]
4
- indent_style = space
5
- indent_size = 2
6
- end_of_line = lf
7
- charset = utf-8
8
- trim_trailing_whitespace = true
9
- insert_final_newline = true
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = space
5
+ indent_size = 2
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
@@ -1,28 +1,28 @@
1
- # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2
- # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3
-
4
- name: Node.js CI
5
-
6
- on:
7
- push:
8
- branches: [master]
9
- pull_request:
10
-
11
- jobs:
12
- build:
13
- runs-on: ubuntu-latest
14
-
15
- strategy:
16
- matrix:
17
- node-version: [14, 16, 18]
18
-
19
- steps:
20
- - uses: actions/checkout@v2
21
- - name: Use Node.js ${{ matrix.node-version }}
22
- uses: actions/setup-node@v1
23
- with:
24
- node-version: ${{ matrix.node-version }}
25
- - run: npm install
26
- - run: npm test
27
- env:
28
- CI: true
1
+ # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2
+ # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3
+
4
+ name: Node.js CI
5
+
6
+ on:
7
+ push:
8
+ branches: [master]
9
+ pull_request:
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+
15
+ strategy:
16
+ matrix:
17
+ node-version: [14, 16, 18]
18
+
19
+ steps:
20
+ - uses: actions/checkout@v2
21
+ - name: Use Node.js ${{ matrix.node-version }}
22
+ uses: actions/setup-node@v1
23
+ with:
24
+ node-version: ${{ matrix.node-version }}
25
+ - run: npm install
26
+ - run: npm test
27
+ env:
28
+ CI: true
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) Cosmin Popovici
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) Cosmin Popovici
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/bin/maizzle CHANGED
@@ -1,3 +1,3 @@
1
- #!/usr/bin/env node
2
-
3
- require('@maizzle/cli')
1
+ #!/usr/bin/env node
2
+
3
+ require('@maizzle/cli')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maizzle/framework",
3
- "version": "4.0.0",
3
+ "version": "4.0.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",
@@ -72,7 +72,7 @@
72
72
  "prevent-widows": "^1.0.2",
73
73
  "query-string": "^7.1.0",
74
74
  "string-strip-html": "^8.2.0",
75
- "tailwindcss": "^3.0.0"
75
+ "tailwindcss": "^3.1.0"
76
76
  },
77
77
  "devDependencies": {
78
78
  "ava": "^4.0.1",
@@ -1,32 +1,32 @@
1
- const path = require('path')
2
- const {merge} = require('lodash')
3
- const {requireUncached} = require('../utils/helpers')
4
-
5
- module.exports = {
6
- getMerged: async (env = 'local') => {
7
- if (typeof env !== 'string') {
8
- throw new TypeError(`env name must be a string, received ${typeof env}(${env})`)
9
- }
10
-
11
- let baseConfig = {env}
12
- let envConfig = {env}
13
-
14
- const cwd = env === 'maizzle-ci' ? './test/stubs/config' : process.cwd()
15
-
16
- for (const module of ['./config', './config.local']) {
17
- try {
18
- baseConfig = merge(baseConfig, requireUncached(path.resolve(cwd, module)))
19
- } catch {}
20
- }
21
-
22
- if (typeof env === 'string' && env !== 'local') {
23
- try {
24
- envConfig = merge(envConfig, requireUncached(path.resolve(cwd, `./config.${env}`)))
25
- } catch {
26
- throw new Error(`could not load config.${env}.js`)
27
- }
28
- }
29
-
30
- return merge(baseConfig, envConfig)
31
- }
32
- }
1
+ const path = require('path')
2
+ const {merge} = require('lodash')
3
+ const {requireUncached} = require('../utils/helpers')
4
+
5
+ module.exports = {
6
+ getMerged: async (env = 'local') => {
7
+ if (typeof env !== 'string') {
8
+ throw new TypeError(`env name must be a string, received ${typeof env}(${env})`)
9
+ }
10
+
11
+ let baseConfig = {env}
12
+ let envConfig = {env}
13
+
14
+ const cwd = env === 'maizzle-ci' ? './test/stubs/config' : process.cwd()
15
+
16
+ for (const module of ['./config', './config.local']) {
17
+ try {
18
+ baseConfig = merge(baseConfig, requireUncached(path.resolve(cwd, module)))
19
+ } catch {}
20
+ }
21
+
22
+ if (typeof env === 'string' && env !== 'local') {
23
+ try {
24
+ envConfig = merge(envConfig, requireUncached(path.resolve(cwd, `./config.${env}`)))
25
+ } catch {
26
+ throw new Error(`could not load config.${env}.js`)
27
+ }
28
+ }
29
+
30
+ return merge(baseConfig, envConfig)
31
+ }
32
+ }
@@ -1,4 +1,4 @@
1
- module.exports = {
2
- toDisk: require('./to-disk'),
3
- toString: require('./to-string')
4
- }
1
+ module.exports = {
2
+ toDisk: require('./to-disk'),
3
+ toString: require('./to-string')
4
+ }
@@ -1,208 +1,208 @@
1
- const path = require('path')
2
- const fs = require('fs-extra')
3
- const glob = require('glob-promise')
4
- const {get, isEmpty, merge} = require('lodash')
5
- const {asyncForEach} = require('../../utils/helpers')
6
- const removePlaintextTags = require('../../transformers/plaintext')
7
-
8
- const Config = require('../config')
9
- const Tailwind = require('../tailwindcss')
10
- const Plaintext = require('../plaintext')
11
-
12
- const render = require('./to-string')
13
-
14
- module.exports = async (env, spinner, config) => {
15
- process.env.NODE_ENV = env || 'local'
16
-
17
- if (isEmpty(config)) {
18
- config = await Config.getMerged(env).catch(error => {
19
- spinner.fail('Build failed')
20
- throw error
21
- })
22
- }
23
-
24
- const buildTemplates = get(config, 'build.templates')
25
- const templatesConfig = Array.isArray(buildTemplates) ? buildTemplates : [buildTemplates]
26
-
27
- const parsed = []
28
- let files = []
29
-
30
- const css = (typeof get(config, 'tailwind.compiled') === 'string')
31
- ? config.tailwind.compiled
32
- : await Tailwind.compile('', '', {}, config, spinner)
33
-
34
- // Parse each template config object
35
- await asyncForEach(templatesConfig, async templateConfig => {
36
- const outputDir = get(templateConfig, 'destination.path', `build_${env}`)
37
-
38
- await fs.remove(outputDir)
39
-
40
- /**
41
- * Get all files in the template config's source directory
42
- * Supports `source` defined as:
43
- * - string
44
- * - array of strings
45
- * - function that returns either of the above
46
- *
47
- * */
48
- const templateSource = []
49
- const templateTypeErrorMessage = 'Invalid template source: expected string or array of strings, got '
50
-
51
- if (typeof templateConfig.source === 'function') {
52
- const sources = templateConfig.source(config)
53
-
54
- if (Array.isArray(sources)) {
55
- templateSource.push(...sources)
56
- } else if (typeof sources === 'string') {
57
- templateSource.push(sources)
58
- } else {
59
- throw new TypeError(templateTypeErrorMessage + typeof sources)
60
- }
61
- } else {
62
- if (Array.isArray(templateConfig.source)) {
63
- templateSource.push(...templateConfig.source)
64
- } else if (typeof templateConfig.source === 'string') {
65
- templateSource.push(templateConfig.source)
66
- } else {
67
- throw new TypeError(templateTypeErrorMessage + typeof templateConfig.source)
68
- }
69
- }
70
-
71
- // Parse each template source
72
- await asyncForEach(templateSource, async source => {
73
- /**
74
- * Copy single-file sources correctly
75
- * If `src` is a file, `dest` cannot be a directory
76
- * https://github.com/jprichardson/node-fs-extra/issues/323
77
- */
78
- const out = fs.lstatSync(source).isFile() ? `${outputDir}/${path.basename(source)}` : outputDir
79
-
80
- await fs
81
- .copy(source, out)
82
- .then(async () => {
83
- const extensions = Array.isArray(templateConfig.filetypes)
84
- ? templateConfig.filetypes.join('|')
85
- : templateConfig.filetypes || get(templateConfig, 'filetypes', 'html')
86
-
87
- const templates = await glob(`${outputDir}/**/*.+(${extensions})`)
88
-
89
- if (templates.length === 0) {
90
- spinner.warn(`Error: no files with the .${extensions} extension found in ${templateConfig.source}`)
91
- return
92
- }
93
-
94
- if (config.events && typeof config.events.beforeCreate === 'function') {
95
- await config.events.beforeCreate(config)
96
- }
97
-
98
- await asyncForEach(templates, async file => {
99
- const html = await fs.readFile(file, 'utf8')
100
-
101
- try {
102
- const compiled = await render(html, {
103
- maizzle: {
104
- ...config,
105
- env
106
- },
107
- tailwind: {
108
- compiled: css
109
- },
110
- ...config.events
111
- })
112
-
113
- const destination = config.permalink || file
114
-
115
- /**
116
- * Generate plaintext
117
- *
118
- * We do this first so that we can remove the <plaintext>
119
- * tags from the markup before outputting the file.
120
- */
121
-
122
- const plaintextConfig = get(templateConfig, 'plaintext')
123
- const plaintextPath = get(plaintextConfig, 'destination.path', config.permalink || file)
124
-
125
- if (Boolean(plaintextConfig) || !isEmpty(plaintextConfig)) {
126
- await Plaintext
127
- .generate(
128
- compiled.html,
129
- plaintextPath,
130
- merge(plaintextConfig, {filepath: file})
131
- )
132
- .then(({plaintext, destination}) => fs.outputFile(destination, plaintext))
133
- }
134
-
135
- compiled.html = removePlaintextTags(compiled.html, config)
136
-
137
- /**
138
- * Output file
139
- */
140
- const parts = path.parse(destination)
141
- const extension = get(templateConfig, 'destination.extension', 'html')
142
- const finalDestination = `${parts.dir}/${parts.name}.${extension}`
143
-
144
- await fs.outputFile(finalDestination, compiled.html)
145
-
146
- /**
147
- * Remove original file if its path is different
148
- * from the final destination path.
149
- *
150
- * This ensures non-HTML files do not pollute
151
- * the build destination folder.
152
- */
153
- if (finalDestination !== file) {
154
- await fs.remove(file)
155
- }
156
-
157
- // Keep track of handled files
158
- files.push(file)
159
- parsed.push(file)
160
- } catch (error) {
161
- switch (config.build.fail) {
162
- case 'silent':
163
- spinner.warn(`Failed to compile template: ${path.basename(file)}`)
164
- break
165
- case 'verbose':
166
- spinner.warn(`Failed to compile template: ${path.basename(file)}`)
167
- console.error(error)
168
- break
169
- default:
170
- spinner.fail(`Failed to compile template: ${path.basename(file)}`)
171
- throw error
172
- }
173
- }
174
- })
175
-
176
- const assets = {source: '', destination: 'assets', ...get(templateConfig, 'assets')}
177
-
178
- if (Array.isArray(assets.source)) {
179
- await asyncForEach(assets.source, async source => {
180
- if (fs.existsSync(source)) {
181
- await fs.copy(source, path.join(templateConfig.destination.path, assets.destination)).catch(error => spinner.warn(error.message))
182
- }
183
- })
184
- } else {
185
- if (fs.existsSync(assets.source)) {
186
- await fs.copy(assets.source, path.join(templateConfig.destination.path, assets.destination)).catch(error => spinner.warn(error.message))
187
- }
188
- }
189
-
190
- await glob(path.join(templateConfig.destination.path, '/**/*.*'))
191
- .then(contents => {
192
- files = [...new Set([...files, ...contents])]
193
- })
194
- })
195
- .catch(error => spinner.warn(error.message))
196
- })
197
- })
198
-
199
- if (config.events && typeof config.events.afterBuild === 'function') {
200
- await config.events.afterBuild(files)
201
- }
202
-
203
- return {
204
- files,
205
- parsed,
206
- css
207
- }
208
- }
1
+ const path = require('path')
2
+ const fs = require('fs-extra')
3
+ const glob = require('glob-promise')
4
+ const {get, isEmpty, merge} = require('lodash')
5
+ const {asyncForEach} = require('../../utils/helpers')
6
+ const removePlaintextTags = require('../../transformers/plaintext')
7
+
8
+ const Config = require('../config')
9
+ const Tailwind = require('../tailwindcss')
10
+ const Plaintext = require('../plaintext')
11
+
12
+ const render = require('./to-string')
13
+
14
+ module.exports = async (env, spinner, config) => {
15
+ process.env.NODE_ENV = env || 'local'
16
+
17
+ if (isEmpty(config)) {
18
+ config = await Config.getMerged(env).catch(error => {
19
+ spinner.fail('Build failed')
20
+ throw error
21
+ })
22
+ }
23
+
24
+ const buildTemplates = get(config, 'build.templates')
25
+ const templatesConfig = Array.isArray(buildTemplates) ? buildTemplates : [buildTemplates]
26
+
27
+ const parsed = []
28
+ let files = []
29
+
30
+ const css = (typeof get(config, 'tailwind.compiled') === 'string')
31
+ ? config.tailwind.compiled
32
+ : await Tailwind.compile('', '', {}, config, spinner)
33
+
34
+ // Parse each template config object
35
+ await asyncForEach(templatesConfig, async templateConfig => {
36
+ const outputDir = get(templateConfig, 'destination.path', `build_${env}`)
37
+
38
+ await fs.remove(outputDir)
39
+
40
+ /**
41
+ * Get all files in the template config's source directory
42
+ * Supports `source` defined as:
43
+ * - string
44
+ * - array of strings
45
+ * - function that returns either of the above
46
+ *
47
+ * */
48
+ const templateSource = []
49
+ const templateTypeErrorMessage = 'Invalid template source: expected string or array of strings, got '
50
+
51
+ if (typeof templateConfig.source === 'function') {
52
+ const sources = templateConfig.source(config)
53
+
54
+ if (Array.isArray(sources)) {
55
+ templateSource.push(...sources)
56
+ } else if (typeof sources === 'string') {
57
+ templateSource.push(sources)
58
+ } else {
59
+ throw new TypeError(templateTypeErrorMessage + typeof sources)
60
+ }
61
+ } else {
62
+ if (Array.isArray(templateConfig.source)) {
63
+ templateSource.push(...templateConfig.source)
64
+ } else if (typeof templateConfig.source === 'string') {
65
+ templateSource.push(templateConfig.source)
66
+ } else {
67
+ throw new TypeError(templateTypeErrorMessage + typeof templateConfig.source)
68
+ }
69
+ }
70
+
71
+ // Parse each template source
72
+ await asyncForEach(templateSource, async source => {
73
+ /**
74
+ * Copy single-file sources correctly
75
+ * If `src` is a file, `dest` cannot be a directory
76
+ * https://github.com/jprichardson/node-fs-extra/issues/323
77
+ */
78
+ const out = fs.lstatSync(source).isFile() ? `${outputDir}/${path.basename(source)}` : outputDir
79
+
80
+ await fs
81
+ .copy(source, out)
82
+ .then(async () => {
83
+ const extensions = Array.isArray(templateConfig.filetypes)
84
+ ? templateConfig.filetypes.join('|')
85
+ : templateConfig.filetypes || get(templateConfig, 'filetypes', 'html')
86
+
87
+ const templates = await glob(`${outputDir}/**/*.+(${extensions})`)
88
+
89
+ if (templates.length === 0) {
90
+ spinner.warn(`Error: no files with the .${extensions} extension found in ${templateConfig.source}`)
91
+ return
92
+ }
93
+
94
+ if (config.events && typeof config.events.beforeCreate === 'function') {
95
+ await config.events.beforeCreate(config)
96
+ }
97
+
98
+ await asyncForEach(templates, async file => {
99
+ const html = await fs.readFile(file, 'utf8')
100
+
101
+ try {
102
+ const compiled = await render(html, {
103
+ maizzle: {
104
+ ...config,
105
+ env
106
+ },
107
+ tailwind: {
108
+ compiled: css
109
+ },
110
+ ...config.events
111
+ })
112
+
113
+ const destination = config.permalink || file
114
+
115
+ /**
116
+ * Generate plaintext
117
+ *
118
+ * We do this first so that we can remove the <plaintext>
119
+ * tags from the markup before outputting the file.
120
+ */
121
+
122
+ const plaintextConfig = get(templateConfig, 'plaintext')
123
+ const plaintextPath = get(plaintextConfig, 'destination.path', config.permalink || file)
124
+
125
+ if (Boolean(plaintextConfig) || !isEmpty(plaintextConfig)) {
126
+ await Plaintext
127
+ .generate(
128
+ compiled.html,
129
+ plaintextPath,
130
+ merge(plaintextConfig, {filepath: file})
131
+ )
132
+ .then(({plaintext, destination}) => fs.outputFile(destination, plaintext))
133
+ }
134
+
135
+ compiled.html = removePlaintextTags(compiled.html, config)
136
+
137
+ /**
138
+ * Output file
139
+ */
140
+ const parts = path.parse(destination)
141
+ const extension = get(templateConfig, 'destination.extension', 'html')
142
+ const finalDestination = `${parts.dir}/${parts.name}.${extension}`
143
+
144
+ await fs.outputFile(finalDestination, compiled.html)
145
+
146
+ /**
147
+ * Remove original file if its path is different
148
+ * from the final destination path.
149
+ *
150
+ * This ensures non-HTML files do not pollute
151
+ * the build destination folder.
152
+ */
153
+ if (finalDestination !== file) {
154
+ await fs.remove(file)
155
+ }
156
+
157
+ // Keep track of handled files
158
+ files.push(file)
159
+ parsed.push(file)
160
+ } catch (error) {
161
+ switch (config.build.fail) {
162
+ case 'silent':
163
+ spinner.warn(`Failed to compile template: ${path.basename(file)}`)
164
+ break
165
+ case 'verbose':
166
+ spinner.warn(`Failed to compile template: ${path.basename(file)}`)
167
+ console.error(error)
168
+ break
169
+ default:
170
+ spinner.fail(`Failed to compile template: ${path.basename(file)}`)
171
+ throw error
172
+ }
173
+ }
174
+ })
175
+
176
+ const assets = {source: '', destination: 'assets', ...get(templateConfig, 'assets')}
177
+
178
+ if (Array.isArray(assets.source)) {
179
+ await asyncForEach(assets.source, async source => {
180
+ if (fs.existsSync(source)) {
181
+ await fs.copy(source, path.join(templateConfig.destination.path, assets.destination)).catch(error => spinner.warn(error.message))
182
+ }
183
+ })
184
+ } else {
185
+ if (fs.existsSync(assets.source)) {
186
+ await fs.copy(assets.source, path.join(templateConfig.destination.path, assets.destination)).catch(error => spinner.warn(error.message))
187
+ }
188
+ }
189
+
190
+ await glob(path.join(templateConfig.destination.path, '/**/*.*'))
191
+ .then(contents => {
192
+ files = [...new Set([...files, ...contents])]
193
+ })
194
+ })
195
+ .catch(error => spinner.warn(error.message))
196
+ })
197
+ })
198
+
199
+ if (config.events && typeof config.events.afterBuild === 'function') {
200
+ await config.events.afterBuild(files)
201
+ }
202
+
203
+ return {
204
+ files,
205
+ parsed,
206
+ css
207
+ }
208
+ }