@maizzle/framework 5.0.0-beta.19 → 5.0.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maizzle/framework",
3
- "version": "5.0.0-beta.19",
3
+ "version": "5.0.0-beta.20",
4
4
  "description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  readFile,
3
3
  writeFile,
4
+ copyFile,
4
5
  lstat,
5
6
  mkdir,
6
7
  rm,
@@ -35,6 +36,27 @@ import {
35
36
 
36
37
  import { readFileConfig } from '../utils/getConfigByFilePath.js'
37
38
 
39
+ /**
40
+ * Ensures that a directory exists, creating it if needed.
41
+ *
42
+ * @param {string} filePath - The path to the file to check.
43
+ */
44
+ async function ensureDirectoryExistence(filePath) {
45
+ const dirname = path.dirname(filePath)
46
+ await mkdir(dirname, { recursive: true })
47
+ }
48
+
49
+ /**
50
+ * Copy a file from source to target.
51
+ *
52
+ * @param {string} source - The source file path.
53
+ * @param {string} target - The target file path.
54
+ */
55
+ async function copyFileAsync(source, target) {
56
+ await ensureDirectoryExistence(target)
57
+ await copyFile(source, target)
58
+ }
59
+
38
60
  /**
39
61
  * Compile templates and output to the build directory.
40
62
  * Returns a promise containing an object with files output and the config object.
@@ -48,7 +70,10 @@ export default async (config = {}) => {
48
70
  try {
49
71
  const startTime = Date.now()
50
72
 
51
- // Compute config
73
+ /**
74
+ * Read the config file for this environment,
75
+ * merging it with the default config.
76
+ */
52
77
  config = await readFileConfig(config).catch(() => { throw new Error('Could not compute config') })
53
78
 
54
79
  /**
@@ -80,17 +105,10 @@ export default async (config = {}) => {
80
105
  })
81
106
 
82
107
  /**
83
- * Determine paths to handle
84
- *
85
- * 1. Resolve globs in `build.content` to folders that should be copied over to `build.output.path`
86
- * 2. Check that templates to be built, actually exist
108
+ * Check that templates to be built, actually exist
87
109
  */
88
- const contentPaths = get(config, 'build.content', 'src/templates/**/*.html')
110
+ const contentPaths = get(config, 'build.content', ['src/templates/**/*.html'])
89
111
 
90
- // 1. Resolve globs in `build.content` to folders that should be copied over to `build.output.path`
91
- const rootDirs = await getRootDirectories(contentPaths)
92
-
93
- // 2. Check that templates to be built, actually exist
94
112
  const templateFolders = Array.isArray(contentPaths) ? contentPaths : [contentPaths]
95
113
  const templatePaths = await fg.glob([...new Set(templateFolders)])
96
114
 
@@ -104,8 +122,48 @@ export default async (config = {}) => {
104
122
  *
105
123
  * Copies each `build.content` path to the `build.output.path` directory.
106
124
  */
107
- for await (const rootDir of rootDirs) {
108
- await cp(rootDir, buildOutputPath, { recursive: true })
125
+ let from = get(config, 'build.output.from', ['src/templates', 'src'])
126
+
127
+ const globPathsToCopy = contentPaths.map(glob => {
128
+ // Keep negated paths as they are
129
+ if (glob.startsWith('!')) {
130
+ return glob
131
+ }
132
+
133
+ // Keep single-file sources as they are
134
+ if (!/\*/.test(glob)) {
135
+ return glob
136
+ }
137
+
138
+ // Update non-negated paths to target all files, avoiding duplication
139
+ return glob.replace(/\/\*\*\/\*\.\{.*?\}$|\/\*\*\/\*\.[^/]*$|\/*\.[^/]*$/, '/**/*')
140
+ })
141
+
142
+ try {
143
+ from = Array.isArray(from) ? from : [from]
144
+
145
+ /**
146
+ * Copy files from source to destination
147
+ *
148
+ * The array/set conversion is to remove duplicates
149
+ */
150
+ for (const file of await fg(Array.from(new Set(globPathsToCopy)))) {
151
+ let relativePath
152
+ for (const dir of from) {
153
+ if (file.startsWith(dir)) {
154
+ relativePath = path.relative(dir, file)
155
+ break
156
+ }
157
+ }
158
+ if (!relativePath) {
159
+ relativePath = path.relative('.', file)
160
+ }
161
+
162
+ const targetPath = path.join(config.build.output.path, relativePath)
163
+ await copyFileAsync(file, targetPath)
164
+ }
165
+ } catch (error) {
166
+ console.error(`Error while processing pattern ${pattern}: `, err);
109
167
  }
110
168
 
111
169
  /**
package/types/build.d.ts CHANGED
@@ -51,6 +51,29 @@ export default interface BuildConfig {
51
51
  * @default 'html'
52
52
  */
53
53
  extension: string;
54
+ /**
55
+ * Path or array of paths that will be unwrapped.
56
+ * Everything inside them will be copied to
57
+ * the root of the output directory.
58
+ *
59
+ * @example
60
+ *
61
+ * ```
62
+ * export default {
63
+ * build: {
64
+ * content: ['test/fixtures/**\/*.html'],
65
+ * output: {
66
+ * from: ['test/fixtures'],
67
+ * }
68
+ * }
69
+ * ```
70
+ *
71
+ * This will copy everything inside `test/fixtures` to the root
72
+ * of the output directory, not creating the `test/fixtures`
73
+ * directory.
74
+ *
75
+ */
76
+ from: string;
54
77
  };
55
78
 
56
79
  /**