@maizzle/framework 4.8.7 → 5.0.0-beta.0

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 (80) hide show
  1. package/bin/maizzle +3 -1
  2. package/package.json +65 -58
  3. package/src/commands/build.js +244 -19
  4. package/src/commands/serve.js +2 -197
  5. package/src/generators/plaintext.js +192 -91
  6. package/src/generators/render.js +128 -0
  7. package/src/index.js +46 -14
  8. package/src/{generators/posthtml → posthtml}/defaultComponentsConfig.js +6 -4
  9. package/src/{generators/posthtml → posthtml}/defaultConfig.js +1 -1
  10. package/src/posthtml/index.js +74 -0
  11. package/src/posthtml/plugins/expandLinkTag.js +36 -0
  12. package/src/server/client.js +181 -0
  13. package/src/server/index.js +383 -0
  14. package/src/server/routes/hmr.js +24 -0
  15. package/src/server/routes/index.js +38 -0
  16. package/src/server/views/error.html +83 -0
  17. package/src/server/views/index.html +24 -0
  18. package/src/server/websockets.js +27 -0
  19. package/src/transformers/addAttributes.js +30 -0
  20. package/src/transformers/attributeToStyle.js +30 -36
  21. package/src/transformers/baseUrl.js +52 -23
  22. package/src/transformers/comb.js +51 -0
  23. package/src/transformers/core.js +20 -0
  24. package/src/transformers/filters/defaultFilters.js +90 -70
  25. package/src/transformers/filters/index.js +14 -78
  26. package/src/transformers/index.js +268 -63
  27. package/src/transformers/inline.js +240 -0
  28. package/src/transformers/markdown.js +13 -14
  29. package/src/transformers/minify.js +21 -16
  30. package/src/transformers/posthtmlMso.js +13 -8
  31. package/src/transformers/prettify.js +16 -15
  32. package/src/transformers/preventWidows.js +32 -26
  33. package/src/transformers/removeAttributes.js +17 -17
  34. package/src/transformers/replaceStrings.js +30 -9
  35. package/src/transformers/safeClassNames.js +24 -24
  36. package/src/transformers/shorthandCss.js +22 -0
  37. package/src/transformers/sixHex.js +15 -15
  38. package/src/transformers/urlParameters.js +18 -16
  39. package/src/transformers/useAttributeSizes.js +65 -0
  40. package/src/utils/getConfigByFilePath.js +124 -0
  41. package/src/utils/node.js +68 -0
  42. package/src/utils/string.js +117 -0
  43. package/types/build.d.ts +117 -57
  44. package/types/components.d.ts +130 -112
  45. package/types/config.d.ts +454 -242
  46. package/types/css/inline.d.ts +234 -0
  47. package/types/css/purge.d.ts +125 -0
  48. package/types/events.d.ts +5 -105
  49. package/types/index.d.ts +148 -116
  50. package/types/markdown.d.ts +20 -18
  51. package/types/minify.d.ts +122 -120
  52. package/types/plaintext.d.ts +46 -52
  53. package/types/posthtml.d.ts +103 -136
  54. package/types/render.d.ts +0 -117
  55. package/types/urlParameters.d.ts +21 -20
  56. package/types/widowWords.d.ts +9 -7
  57. package/src/functions/plaintext.js +0 -5
  58. package/src/functions/render.js +0 -5
  59. package/src/generators/config.js +0 -52
  60. package/src/generators/output/index.js +0 -4
  61. package/src/generators/output/to-disk.js +0 -254
  62. package/src/generators/output/to-string.js +0 -73
  63. package/src/generators/postcss.js +0 -23
  64. package/src/generators/posthtml/index.js +0 -75
  65. package/src/generators/tailwindcss.js +0 -157
  66. package/src/transformers/extraAttributes.js +0 -33
  67. package/src/transformers/inlineCss.js +0 -42
  68. package/src/transformers/removeInlineBackgroundColor.js +0 -56
  69. package/src/transformers/removeInlineSizes.js +0 -43
  70. package/src/transformers/removeInlinedSelectors.js +0 -100
  71. package/src/transformers/removeUnusedCss.js +0 -48
  72. package/src/transformers/shorthandInlineCSS.js +0 -26
  73. package/src/utils/helpers.js +0 -13
  74. package/types/baseUrl.d.ts +0 -79
  75. package/types/fetch.d.ts +0 -143
  76. package/types/inlineCss.d.ts +0 -207
  77. package/types/layouts.d.ts +0 -39
  78. package/types/removeUnusedCss.d.ts +0 -115
  79. package/types/tailwind.d.ts +0 -22
  80. package/types/templates.d.ts +0 -181
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Inject a script into HTML by "prepending" it to the first available closing
3
+ * tag from a list of candidates.
4
+ *
5
+ * @param {string} html The HTML content
6
+ * @param {string} script The script to inject
7
+ * @returns {string} The modified HTML
8
+ */
9
+ export function injectScript(html = '', script = '') {
10
+ if (html.includes('</head>')) {
11
+ return html.replace('</head>', `${script}</head>`)
12
+ }
13
+
14
+ if (html.includes('</title>')) {
15
+ return html.replace('</title>', `</title>${script}`)
16
+ }
17
+
18
+ if (html.includes('</body>')) {
19
+ return html.replace('</body>', `${script}</body>`)
20
+ }
21
+
22
+ if (html.includes('</html>')) {
23
+ return html.replace('</html>', `${script}</html>`)
24
+ }
25
+
26
+ if (html.includes('<!doctype html>')) {
27
+ return html.replace('<!doctype html>', `<!doctype html>${script}`)
28
+ }
29
+
30
+ return script + html
31
+ }
32
+
33
+ /**
34
+ * Find the common prefix among an array of strings.
35
+ *
36
+ * @param {string[]} strings Array of strings
37
+ * @returns {string} Common prefix
38
+ * @throws {TypeError} If the input is not an array
39
+ */
40
+ export function findCommonPrefix(strings) {
41
+ // Must be an array
42
+ if (!Array.isArray(strings)) {
43
+ throw new TypeError('findCommonPrefix expects an array')
44
+ }
45
+
46
+ const sortedStrings = strings.slice().sort()
47
+ const first = sortedStrings[0]
48
+ const last = sortedStrings[sortedStrings.length - 1]
49
+ let i = 0
50
+
51
+ while (i < first.length && first.charAt(i) === last.charAt(i)) {
52
+ i++
53
+ }
54
+
55
+ return first.substring(0, i)
56
+ }
57
+
58
+ export function formatMs(milliseconds) {
59
+ const date = new Date(milliseconds);
60
+
61
+ const hours = date.getUTCHours();
62
+ const minutes = date.getUTCMinutes();
63
+ const seconds = date.getUTCSeconds();
64
+ const formattedTime = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
65
+
66
+ return formattedTime;
67
+ }
68
+
69
+ /**
70
+ * Format milliseconds as human-readable text.
71
+ *
72
+ * @param {number} ms Number of milliseconds.
73
+ * @returns {string} Formatted string.
74
+ */
75
+ export function formatTime(ms) {
76
+ if (ms < 1000) {
77
+ return `${ms} ms`
78
+ }
79
+
80
+ if (ms < 60000) { // Less than 1 minute
81
+ const seconds = ms / 1000
82
+ return `${seconds.toFixed(2)} s`
83
+ }
84
+
85
+ const minutes = ms / 60000
86
+ return `${minutes.toFixed(2)} min`
87
+ }
88
+
89
+ /**
90
+ * Format bytes as human-readable text.
91
+ *
92
+ * @param {number} bytes Number of bytes.
93
+ * @param {boolean} si True to use metric (SI) units, aka powers of 1000. False to use
94
+ * binary (IEC), aka powers of 1024.
95
+ * @param {number} dp Number of decimal places to display.
96
+ *
97
+ * @return {string} Formatted string.
98
+ */
99
+ export function humanFileSize(bytes, si=false, dp=2) {
100
+ const threshold = si ? 1000 : 1024
101
+
102
+ if (Math.abs(bytes) < threshold) {
103
+ return bytes + ' B'
104
+ }
105
+
106
+ const units = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
107
+ let u = -1
108
+ const r = 10**dp
109
+
110
+ do {
111
+ bytes /= threshold
112
+ ++u
113
+ } while (Math.round(Math.abs(bytes) * r) / r >= threshold && u < units.length - 1)
114
+
115
+
116
+ return bytes.toFixed(dp) + ' ' + units[u]
117
+ }
package/types/build.d.ts CHANGED
@@ -1,74 +1,134 @@
1
- import type LayoutsConfig from './layouts';
2
- import type PostHTMLConfig from './posthtml';
3
- import type TailwindConfig from './tailwind';
4
- import type TemplatesConfig from './templates';
5
- import type ComponentsConfig from './components';
6
- import type {Options as BrowserSyncConfig} from 'browser-sync';
1
+ import ComponentsConfig from './components';
2
+ import type { SpinnerName } from 'cli-spinners';
7
3
 
8
4
  export default interface BuildConfig {
9
5
  /**
10
- Templates configuration.
11
- */
12
- templates: TemplatesConfig;
13
-
14
- /**
15
- Tailwind CSS configuration.
16
- */
17
- tailwind?: TailwindConfig;
18
-
19
- /**
20
- [DEPRECATED] Layouts configuration.
21
- */
22
- layouts?: LayoutsConfig;
23
-
24
- /**
25
- Components configuration.
26
- */
6
+ * Components configuration.
7
+ */
27
8
  components?: ComponentsConfig;
28
9
 
29
10
  /**
30
- PostHTML configuration.
31
- */
32
- posthtml?: PostHTMLConfig;
11
+ * Directory where Maizzle should look for Templates to compile.
12
+ *
13
+ * @default ['src/templates/**\/*.html']
14
+ *
15
+ * @example
16
+ * ```
17
+ * export default {
18
+ * build: {
19
+ * files: ['src/templates/**\/*.html']
20
+ * }
21
+ * }
22
+ * ```
23
+ */
24
+ files?: string | string[];
33
25
 
34
26
  /**
35
- Configure PostCSS
27
+ * Define the output path for compiled Templates, and what file extension they should use.
28
+ *
29
+ * @example
30
+ * ```
31
+ * export default {
32
+ * build: {
33
+ * output: {
34
+ * path: 'build_production',
35
+ * extension: 'html'
36
+ * }
37
+ * }
38
+ * }
39
+ * ```
36
40
  */
37
- postcss?: {
41
+ output?: {
38
42
  /**
39
- Additional PostCSS plugins that you would like to use.
40
-
41
- @default []
42
-
43
- @example
44
- ```
45
- const examplePlugin = require('postcss-example-plugin')
46
- module.exports = {
47
- build: {
48
- postcss: {
49
- plugins: [
50
- examplePlugin()
51
- ]
52
- }
53
- }
54
- }
55
- ```
56
- */
57
- plugins?: any[];
43
+ * Directory where Maizzle should output compiled Templates.
44
+ *
45
+ * @default 'build_{env}'
46
+ */
47
+ path?: string;
48
+ /**
49
+ * File extension to be used for compiled Templates.
50
+ *
51
+ * @default 'html'
52
+ */
53
+ extension: string;
58
54
  };
59
55
 
60
56
  /**
61
- Browsersync configuration.
62
-
63
- When you run the `maizzle serve` command, Maizzle uses [Browsersync](https://browsersync.io/)
64
- to start a local development server and open a directory listing of your emails in your default browser.
65
- */
66
- browsersync?: BrowserSyncConfig;
57
+ * Source and destination directories for static files.
58
+ *
59
+ * @example
60
+ * ```
61
+ * export default {
62
+ * build: {
63
+ * static: {
64
+ * source: ['src/images/**\/*.*'],
65
+ * destination: 'images'
66
+ * }
67
+ * }
68
+ * }
69
+ * ```
70
+ */
71
+ static?: {
72
+ /**
73
+ * Array of paths where Maizzle should look for static files.
74
+ *
75
+ * @default undefined
76
+ */
77
+ source?: string[];
78
+ /**
79
+ * Directory where static files should be copied to,
80
+ * relative to the `build.output` path.
81
+ *
82
+ * @default undefined
83
+ */
84
+ destination?: string;
85
+ } | {
86
+ /**
87
+ * An array of objects specifying source and destination directories for static files.
88
+ */
89
+ static: Array<{
90
+ /**
91
+ * Array of paths where Maizzle should look for static files.
92
+ */
93
+ source: string[];
94
+ /**
95
+ * Directory where static files should be copied to,
96
+ * relative to the `build.output` path.
97
+ */
98
+ destination: string;
99
+ }>;
100
+ };
67
101
 
68
102
  /**
69
- Configure how build errors are handled when developing with the Maizzle CLI.
103
+ * Type of spinner to show in the console.
104
+ *
105
+ * @default 'dots'
106
+ *
107
+ * @example
108
+ * ```
109
+ * export default {
110
+ * build: {
111
+ * spinner: 'bounce'
112
+ * }
113
+ * }
114
+ * ```
115
+ */
116
+ spinner?: SpinnerName;
70
117
 
71
- @default undefined
72
- */
73
- fail?: 'silent' | 'verbose';
118
+ /**
119
+ * Show a summary of files that were compiled, along with their
120
+ * size and the time it took to compile them.
121
+ *
122
+ * @default false
123
+ *
124
+ * @example
125
+ * ```
126
+ * export default {
127
+ * build: {
128
+ * summary: true
129
+ * }
130
+ * }
131
+ * ```
132
+ */
133
+ summary?: boolean;
74
134
  }
@@ -1,177 +1,195 @@
1
+ import type ExpressionsConfig from './expressions';
2
+ import type { Options as PostHTMLParserOptions } from 'posthtml-parser';
3
+
4
+ interface AnyObject {
5
+ [key: string]: string | AnyObject;
6
+ }
7
+
1
8
  export default interface ComponentsConfig {
2
9
  /**
3
- Root path where to look for folders containing component files.
4
-
5
- @default './'
6
- */
10
+ * Root path where to look for folders containing component files.
11
+ *
12
+ * @default './'
13
+ */
7
14
  root?: string;
8
15
 
9
16
  /**
10
- Paths where to look for component files. Must be relative to `root`.
11
-
12
- @default ['src/components', 'src/layouts', 'src/templates']
13
- */
17
+ * Paths where to look for component files. Must be relative to `root`.
18
+ *
19
+ * @default ['src/components', 'src/layouts', 'src/templates']
20
+ */
14
21
  folders?: string[];
15
22
 
16
23
  /**
17
- Prefix to use for component tags.
18
-
19
- @default 'x-'
20
- */
24
+ * Prefix to use for component tags.
25
+ *
26
+ * @default 'x-'
27
+ */
21
28
  tagPrefix?: string;
22
29
 
23
30
  /**
24
- Tag name to be used in HTML when using a component.
25
-
26
- @default 'component'
27
- */
31
+ * Tag name to be used in HTML when using a component.
32
+ *
33
+ * @default 'component'
34
+ */
28
35
  tag?: string;
29
36
 
30
37
  /**
31
- Attribute name to be used when referencing a component via its path.
32
-
33
- @default 'src'
34
- */
38
+ * Attribute name to be used when referencing a component via its path.
39
+ *
40
+ * @default 'src'
41
+ */
35
42
  attribute?: string;
36
43
 
37
44
  /**
38
- File extension that component files must use.
39
- Any other files will be ignored and not be made available as components.
40
-
41
- @default 'html'
42
- */
45
+ * File extension that component files must use.
46
+ * Any other files will be ignored and not be made available as components.
47
+ *
48
+ * @default 'html'
49
+ */
43
50
  fileExtension?: string;
44
51
 
45
52
  /**
46
- Name of the tag that will be replaced with the content that is passed to the component.
47
-
48
- @default 'content'
49
- */
53
+ * Name of the tag that will be replaced with the content that is passed to the component.
54
+ *
55
+ * @default 'yield'
56
+ */
50
57
  yield?: string;
51
58
 
52
59
  /**
53
- Name of the slot tag, where the content will be injected.
54
-
55
- @default 'slot'
60
+ * Name of the slot tag, where the content will be injected.
61
+ *
62
+ * @default 'slot'
56
63
  */
57
64
  slot?: string;
58
65
 
59
66
  /**
60
- Name of the fill tag, where the content to be injected is defined.
61
-
62
- @default 'fill'
63
- */
67
+ * Name of the fill tag, where the content to be injected is defined.
68
+ *
69
+ * @default 'fill'
70
+ */
64
71
  fill?: string;
65
72
 
66
73
  /**
67
- String to use as a separator between the slot tag and its name.
68
-
69
- @default ':'
70
- */
74
+ * String to use as a separator between the slot tag and its name.
75
+ *
76
+ * @default ':'
77
+ */
71
78
  slotSeparator?: string;
72
79
 
73
80
  /**
74
- Tag name for pushing content to a stack.
75
-
76
- @default 'push'
77
- */
81
+ * Tag name for pushing content to a stack.
82
+ *
83
+ * @default 'push'
84
+ */
78
85
  push?: string;
79
86
 
80
87
  /**
81
- Tag name for popping (rendering) content from a stack.
82
-
83
- @default 'stack'
84
- */
88
+ * Tag name for popping (rendering) content from a stack.
89
+ *
90
+ * @default 'stack'
91
+ */
85
92
  stack?: string;
86
93
 
87
94
  /**
88
- Name of the props attribute to use in the `<script>` tag of a component.
89
-
90
- @default 'props'
91
- */
95
+ * Name of the props attribute to use in the `<script>` tag of a component.
96
+ *
97
+ * @default 'props'
98
+ */
92
99
  propsScriptAttribute?: string;
93
100
 
94
101
  /**
95
- Name of the object that will be used to store the props of a component.
96
-
97
- @default 'props'
98
- */
102
+ * Name of the object that will be used to store the props of a component.
103
+ *
104
+ * @default 'props'
105
+ */
99
106
  propsContext?: string;
100
107
 
101
108
  /**
102
- Name of the attribute that will be used to pass props to a component as JSON.
103
-
104
- @default 'locals'
105
- */
109
+ * Name of the attribute that will be used to pass props to a component as JSON.
110
+ *
111
+ * @default 'locals'
112
+ */
106
113
  propsAttribute?: string;
107
114
 
108
115
  /**
109
- Name of the key to use when retrieving props passed to a slot via `$slots.slotName.props`.
110
-
111
- @default 'props'
112
- */
116
+ * Name of the key to use when retrieving props passed to a slot via `$slots.slotName.props`.
117
+ *
118
+ * @default 'props'
119
+ */
113
120
  propsSlot?: string;
114
121
 
115
122
  /**
116
- Configure [`posthtml-parser`](https://github.com/posthtml/posthtml-parser).
117
-
118
- @default {recognizeSelfClosing:true}
119
- */
120
- parserOptions?: Record<string, any>;
121
-
122
- /**
123
- Configure [`posthtml-expressions`](https://github.com/posthtml/posthtml-expressions).
124
-
125
- @default {} // custom object
126
- */
127
- expressions?: Record<any, any>;
128
-
129
- /**
130
- PostHTML plugins to apply to each parsed component.
131
-
132
- @default []
133
- */
134
- plugins?: any[];
135
-
136
- /**
137
- Extra rules for the PostHTML plugin that is used by components to parse attributes.
138
-
139
- @default {}
140
- */
141
- attrsParserRules?: Record<any, any>;
142
-
143
- /**
144
- In strict mode, an error will be thrown if a component cannot be rendered.
145
-
146
- @default true
147
- */
123
+ * Configure [`posthtml-parser`](https://github.com/posthtml/posthtml-parser).
124
+ *
125
+ * @default
126
+ * {
127
+ * recognizeNoValueAttribute: true,
128
+ * recognizeSelfClosing: true
129
+ * }
130
+ */
131
+ parserOptions?: PostHTMLParserOptions;
132
+
133
+ /**
134
+ * Configure [`posthtml-expressions`](https://github.com/posthtml/posthtml-expressions).
135
+ *
136
+ * @default
137
+ * {
138
+ * strictMode: false,
139
+ * missingLocal: '{local}',
140
+ * locals: {
141
+ * page: config, // the computed Maizzle config object
142
+ * }
143
+ * }
144
+ */
145
+ expressions?: ExpressionsConfig;
146
+
147
+ /**
148
+ * PostHTML plugins to apply to each parsed component.
149
+ *
150
+ * @default []
151
+ */
152
+ plugins?: Array<() => void>;
153
+
154
+ /**
155
+ * Extra rules for the PostHTML plugin that is used by components to parse attributes.
156
+ *
157
+ * @default {}
158
+ */
159
+ attrsParserRules?: Record<string, AnyObject>;
160
+
161
+ /**
162
+ * In strict mode, an error will be thrown if a component cannot be rendered.
163
+ *
164
+ * @default true
165
+ */
148
166
  strict?: boolean;
149
167
 
150
168
  /**
151
- Utility methods to be passed to `<script props>` in a component.
152
-
153
- @default {merge: _.mergeWith, template: _.template}
154
- */
169
+ * Utility methods to be passed to `<script props>` in a component.
170
+ *
171
+ * @default {merge: _.mergeWith, template: _.template}
172
+ */
155
173
  utilities?: Record<string, unknown>;
156
174
 
157
175
  /**
158
- Define additional attributes that should be preserved for specific HTML elements.
159
-
160
- @default {}
161
- */
176
+ * Define additional attributes that should be preserved for specific HTML elements.
177
+ *
178
+ * @default {}
179
+ */
162
180
  elementAttributes?: Record<string, void>;
163
181
 
164
182
  /**
165
- Attributes that should be preserved on all elements in components.
166
-
167
- @default ['data-*']
168
- */
183
+ * Attributes that should be preserved on all elements in components.
184
+ *
185
+ * @default ['data-*']
186
+ */
169
187
  safelistAttributes?: string[];
170
188
 
171
189
  /**
172
- Attributes that should be removed from all elements in components.
173
-
174
- @default []
175
- */
190
+ * Attributes that should be removed from all elements in components.
191
+ *
192
+ * @default []
193
+ */
176
194
  blacklistAttributes?: string[];
177
195
  }